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(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,
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' && !this.allowBlank){
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' && !this.allowBlank){
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);
12946 filterValidation : function(e){
12947 if(!e.isNavKeyPress()){
12948 this.validationTask.delay(this.validationDelay);
12952 * Validates the field value
12953 * @return {Boolean} True if the value is valid, else false
12955 validate : function(){
12956 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12957 if(this.disabled || this.validateValue(this.getRawValue())){
12958 Roo.log('MARK INVALID');
12963 this.markInvalid();
12969 * Validates a value according to the field's validation rules and marks the field as invalid
12970 * if the validation fails
12971 * @param {Mixed} value The value to validate
12972 * @return {Boolean} True if the value is valid, else false
12974 validateValue : function(value)
12976 if(this.getVisibilityEl().hasClass('hidden')){
12980 if(value.length < 1) { // if it's blank
12981 if(this.allowBlank){
12987 if(value.length < this.minLength){
12990 if(value.length > this.maxLength){
12994 var vt = Roo.form.VTypes;
12995 if(!vt[this.vtype](value, this)){
12999 if(typeof this.validator == "function"){
13000 var msg = this.validator(value);
13001 if (typeof(msg) == 'string') {
13002 this.invalidText = msg;
13009 if(this.regex && !this.regex.test(value)){
13017 fireKey : function(e){
13018 //Roo.log('field ' + e.getKey());
13019 if(e.isNavKeyPress()){
13020 this.fireEvent("specialkey", this, e);
13023 focus : function (selectText){
13025 this.inputEl().focus();
13026 if(selectText === true){
13027 this.inputEl().dom.select();
13033 onFocus : function(){
13034 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13035 // this.el.addClass(this.focusClass);
13037 if(!this.hasFocus){
13038 this.hasFocus = true;
13039 this.startValue = this.getValue();
13040 this.fireEvent("focus", this);
13044 beforeBlur : Roo.emptyFn,
13048 onBlur : function(){
13050 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13051 //this.el.removeClass(this.focusClass);
13053 this.hasFocus = false;
13054 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13057 var v = this.getValue();
13058 if(String(v) !== String(this.startValue)){
13059 this.fireEvent('change', this, v, this.startValue);
13061 this.fireEvent("blur", this);
13064 onChange : function(e)
13066 var v = this.getValue();
13067 if(String(v) !== String(this.startValue)){
13068 this.fireEvent('change', this, v, this.startValue);
13074 * Resets the current field value to the originally loaded value and clears any validation messages
13076 reset : function(){
13077 this.setValue(this.originalValue);
13081 * Returns the name of the field
13082 * @return {Mixed} name The name field
13084 getName: function(){
13088 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13089 * @return {Mixed} value The field value
13091 getValue : function(){
13093 var v = this.inputEl().getValue();
13098 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13099 * @return {Mixed} value The field value
13101 getRawValue : function(){
13102 var v = this.inputEl().getValue();
13108 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13109 * @param {Mixed} value The value to set
13111 setRawValue : function(v){
13112 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13115 selectText : function(start, end){
13116 var v = this.getRawValue();
13118 start = start === undefined ? 0 : start;
13119 end = end === undefined ? v.length : end;
13120 var d = this.inputEl().dom;
13121 if(d.setSelectionRange){
13122 d.setSelectionRange(start, end);
13123 }else if(d.createTextRange){
13124 var range = d.createTextRange();
13125 range.moveStart("character", start);
13126 range.moveEnd("character", v.length-end);
13133 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13134 * @param {Mixed} value The value to set
13136 setValue : function(v){
13139 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13145 processValue : function(value){
13146 if(this.stripCharsRe){
13147 var newValue = value.replace(this.stripCharsRe, '');
13148 if(newValue !== value){
13149 this.setRawValue(newValue);
13156 preFocus : function(){
13158 if(this.selectOnFocus){
13159 this.inputEl().dom.select();
13162 filterKeys : function(e){
13163 var k = e.getKey();
13164 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13167 var c = e.getCharCode(), cc = String.fromCharCode(c);
13168 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13171 if(!this.maskRe.test(cc)){
13176 * Clear any invalid styles/messages for this field
13178 clearInvalid : function(){
13180 if(!this.el || this.preventMark){ // not rendered
13185 this.el.removeClass([this.invalidClass, 'is-invalid']);
13187 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13189 var feedback = this.el.select('.form-control-feedback', true).first();
13192 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13197 if(this.indicator){
13198 this.indicator.removeClass('visible');
13199 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13202 this.fireEvent('valid', this);
13206 * Mark this field as valid
13208 markValid : function()
13210 if(!this.el || this.preventMark){ // not rendered...
13214 this.el.removeClass([this.invalidClass, this.validClass]);
13215 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13217 var feedback = this.el.select('.form-control-feedback', true).first();
13220 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13223 if(this.indicator){
13224 this.indicator.removeClass('visible');
13225 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13233 if(this.allowBlank && !this.getRawValue().length){
13236 if (Roo.bootstrap.version == 3) {
13237 this.el.addClass(this.validClass);
13239 this.inputEl().addClass('is-valid');
13242 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13244 var feedback = this.el.select('.form-control-feedback', true).first();
13247 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13248 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13253 this.fireEvent('valid', this);
13257 * Mark this field as invalid
13258 * @param {String} msg The validation message
13260 markInvalid : function(msg)
13262 if(!this.el || this.preventMark){ // not rendered
13266 this.el.removeClass([this.invalidClass, this.validClass]);
13267 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13269 var feedback = this.el.select('.form-control-feedback', true).first();
13272 this.el.select('.form-control-feedback', true).first().removeClass(
13273 [this.invalidFeedbackClass, this.validFeedbackClass]);
13280 if(this.allowBlank && !this.getRawValue().length){
13284 if(this.indicator){
13285 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13286 this.indicator.addClass('visible');
13288 if (Roo.bootstrap.version == 3) {
13289 this.el.addClass(this.invalidClass);
13291 this.inputEl().addClass('is-invalid');
13296 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13298 var feedback = this.el.select('.form-control-feedback', true).first();
13301 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13303 if(this.getValue().length || this.forceFeedback){
13304 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13311 this.fireEvent('invalid', this, msg);
13314 SafariOnKeyDown : function(event)
13316 // this is a workaround for a password hang bug on chrome/ webkit.
13317 if (this.inputEl().dom.type != 'password') {
13321 var isSelectAll = false;
13323 if(this.inputEl().dom.selectionEnd > 0){
13324 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13326 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13327 event.preventDefault();
13332 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13334 event.preventDefault();
13335 // this is very hacky as keydown always get's upper case.
13337 var cc = String.fromCharCode(event.getCharCode());
13338 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13342 adjustWidth : function(tag, w){
13343 tag = tag.toLowerCase();
13344 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13345 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13346 if(tag == 'input'){
13349 if(tag == 'textarea'){
13352 }else if(Roo.isOpera){
13353 if(tag == 'input'){
13356 if(tag == 'textarea'){
13364 setFieldLabel : function(v)
13366 if(!this.rendered){
13370 if(this.indicatorEl()){
13371 var ar = this.el.select('label > span',true);
13373 if (ar.elements.length) {
13374 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13375 this.fieldLabel = v;
13379 var br = this.el.select('label',true);
13381 if(br.elements.length) {
13382 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13383 this.fieldLabel = v;
13387 Roo.log('Cannot Found any of label > span || label in input');
13391 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13392 this.fieldLabel = v;
13407 * @class Roo.bootstrap.form.TextArea
13408 * @extends Roo.bootstrap.form.Input
13409 * Bootstrap TextArea class
13410 * @cfg {Number} cols Specifies the visible width of a text area
13411 * @cfg {Number} rows Specifies the visible number of lines in a text area
13412 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13413 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13414 * @cfg {string} html text
13417 * Create a new TextArea
13418 * @param {Object} config The config object
13421 Roo.bootstrap.form.TextArea = function(config){
13422 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13426 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13436 getAutoCreate : function(){
13438 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13444 if(this.inputType != 'hidden'){
13445 cfg.cls = 'form-group' //input-group
13453 value : this.value || '',
13454 html: this.html || '',
13455 cls : 'form-control',
13456 placeholder : this.placeholder || ''
13460 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13461 input.maxLength = this.maxLength;
13465 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13469 input.cols = this.cols;
13472 if (this.readOnly) {
13473 input.readonly = true;
13477 input.name = this.name;
13481 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13485 ['xs','sm','md','lg'].map(function(size){
13486 if (settings[size]) {
13487 cfg.cls += ' col-' + size + '-' + settings[size];
13491 var inputblock = input;
13493 if(this.hasFeedback && !this.allowBlank){
13497 cls: 'glyphicon form-control-feedback'
13501 cls : 'has-feedback',
13510 if (this.before || this.after) {
13513 cls : 'input-group',
13517 inputblock.cn.push({
13519 cls : 'input-group-addon',
13524 inputblock.cn.push(input);
13526 if(this.hasFeedback && !this.allowBlank){
13527 inputblock.cls += ' has-feedback';
13528 inputblock.cn.push(feedback);
13532 inputblock.cn.push({
13534 cls : 'input-group-addon',
13542 cfg = this.getAutoCreateLabel( cfg, inputblock );
13546 if (this.disabled) {
13547 input.disabled=true;
13554 * return the real textarea element.
13556 inputEl: function ()
13558 return this.el.select('textarea.form-control',true).first();
13562 * Clear any invalid styles/messages for this field
13564 clearInvalid : function()
13567 if(!this.el || this.preventMark){ // not rendered
13571 var label = this.el.select('label', true).first();
13572 //var icon = this.el.select('i.fa-star', true).first();
13574 //if(label && icon){
13577 this.el.removeClass( this.validClass);
13578 this.inputEl().removeClass('is-invalid');
13580 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13582 var feedback = this.el.select('.form-control-feedback', true).first();
13585 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13590 this.fireEvent('valid', this);
13594 * Mark this field as valid
13596 markValid : function()
13598 if(!this.el || this.preventMark){ // not rendered
13602 this.el.removeClass([this.invalidClass, this.validClass]);
13603 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13605 var feedback = this.el.select('.form-control-feedback', true).first();
13608 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13611 if(this.disabled || this.allowBlank){
13615 var label = this.el.select('label', true).first();
13616 var icon = this.el.select('i.fa-star', true).first();
13618 //if(label && icon){
13621 if (Roo.bootstrap.version == 3) {
13622 this.el.addClass(this.validClass);
13624 this.inputEl().addClass('is-valid');
13628 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13630 var feedback = this.el.select('.form-control-feedback', true).first();
13633 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13634 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13639 this.fireEvent('valid', this);
13643 * Mark this field as invalid
13644 * @param {String} msg The validation message
13646 markInvalid : function(msg)
13648 if(!this.el || this.preventMark){ // not rendered
13652 this.el.removeClass([this.invalidClass, this.validClass]);
13653 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13655 var feedback = this.el.select('.form-control-feedback', true).first();
13658 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13661 if(this.disabled || this.allowBlank){
13665 var label = this.el.select('label', true).first();
13666 //var icon = this.el.select('i.fa-star', true).first();
13668 //if(!this.getValue().length && label && !icon){
13669 /* this.el.createChild({
13671 cls : 'text-danger fa fa-lg fa-star',
13672 tooltip : 'This field is required',
13673 style : 'margin-right:5px;'
13678 if (Roo.bootstrap.version == 3) {
13679 this.el.addClass(this.invalidClass);
13681 this.inputEl().addClass('is-invalid');
13684 // fixme ... this may be depricated need to test..
13685 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13687 var feedback = this.el.select('.form-control-feedback', true).first();
13690 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13692 if(this.getValue().length || this.forceFeedback){
13693 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13700 this.fireEvent('invalid', this, msg);
13708 * trigger field - base class for combo..
13713 * @class Roo.bootstrap.form.TriggerField
13714 * @extends Roo.bootstrap.form.Input
13715 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13716 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13717 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13718 * for which you can provide a custom implementation. For example:
13720 var trigger = new Roo.bootstrap.form.TriggerField();
13721 trigger.onTriggerClick = myTriggerFn;
13722 trigger.applyTo('my-field');
13725 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13726 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13727 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13728 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13729 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13732 * Create a new TriggerField.
13733 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13734 * to the base TextField)
13736 Roo.bootstrap.form.TriggerField = function(config){
13737 this.mimicing = false;
13738 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13741 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13743 * @cfg {String} triggerClass A CSS class to apply to the trigger
13746 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13751 * @cfg {Boolean} removable (true|false) special filter default false
13755 /** @cfg {Boolean} grow @hide */
13756 /** @cfg {Number} growMin @hide */
13757 /** @cfg {Number} growMax @hide */
13763 autoSize: Roo.emptyFn,
13767 deferHeight : true,
13770 actionMode : 'wrap',
13775 getAutoCreate : function(){
13777 var align = this.labelAlign || this.parentLabelAlign();
13782 cls: 'form-group' //input-group
13789 type : this.inputType,
13790 cls : 'form-control',
13791 autocomplete: 'new-password',
13792 placeholder : this.placeholder || ''
13796 input.name = this.name;
13799 input.cls += ' input-' + this.size;
13802 if (this.disabled) {
13803 input.disabled=true;
13806 var inputblock = input;
13808 if(this.hasFeedback && !this.allowBlank){
13812 cls: 'glyphicon form-control-feedback'
13815 if(this.removable && !this.editable ){
13817 cls : 'has-feedback',
13823 cls : 'roo-combo-removable-btn close'
13830 cls : 'has-feedback',
13839 if(this.removable && !this.editable ){
13841 cls : 'roo-removable',
13847 cls : 'roo-combo-removable-btn close'
13854 if (this.before || this.after) {
13857 cls : 'input-group',
13861 inputblock.cn.push({
13863 cls : 'input-group-addon input-group-prepend input-group-text',
13868 inputblock.cn.push(input);
13870 if(this.hasFeedback && !this.allowBlank){
13871 inputblock.cls += ' has-feedback';
13872 inputblock.cn.push(feedback);
13876 inputblock.cn.push({
13878 cls : 'input-group-addon input-group-append input-group-text',
13887 var ibwrap = inputblock;
13892 cls: 'roo-select2-choices',
13896 cls: 'roo-select2-search-field',
13908 cls: 'roo-select2-container input-group',
13913 cls: 'form-hidden-field'
13919 if(!this.multiple && this.showToggleBtn){
13925 if (this.caret != false) {
13928 cls: 'fa fa-' + this.caret
13935 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13937 Roo.bootstrap.version == 3 ? caret : '',
13940 cls: 'combobox-clear',
13954 combobox.cls += ' roo-select2-container-multi';
13958 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13959 tooltip : 'This field is required'
13962 if (this.allowBlank) {
13965 style : 'display:none'
13971 if (align ==='left' && this.fieldLabel.length) {
13973 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13980 cls : 'control-label',
13981 html : this.fieldLabel
13993 var labelCfg = cfg.cn[1];
13994 var contentCfg = cfg.cn[2];
13996 if(this.indicatorpos == 'right'){
14001 cls : 'control-label',
14005 html : this.fieldLabel
14019 labelCfg = cfg.cn[0];
14020 contentCfg = cfg.cn[1];
14023 if(this.labelWidth > 12){
14024 labelCfg.style = "width: " + this.labelWidth + 'px';
14027 if(this.labelWidth < 13 && this.labelmd == 0){
14028 this.labelmd = this.labelWidth;
14031 if(this.labellg > 0){
14032 labelCfg.cls += ' col-lg-' + this.labellg;
14033 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14036 if(this.labelmd > 0){
14037 labelCfg.cls += ' col-md-' + this.labelmd;
14038 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14041 if(this.labelsm > 0){
14042 labelCfg.cls += ' col-sm-' + this.labelsm;
14043 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14046 if(this.labelxs > 0){
14047 labelCfg.cls += ' col-xs-' + this.labelxs;
14048 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14051 } else if ( this.fieldLabel.length) {
14052 // Roo.log(" label");
14057 //cls : 'input-group-addon',
14058 html : this.fieldLabel
14066 if(this.indicatorpos == 'right'){
14074 html : this.fieldLabel
14088 // Roo.log(" no label && no align");
14095 ['xs','sm','md','lg'].map(function(size){
14096 if (settings[size]) {
14097 cfg.cls += ' col-' + size + '-' + settings[size];
14108 onResize : function(w, h){
14109 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14110 // if(typeof w == 'number'){
14111 // var x = w - this.trigger.getWidth();
14112 // this.inputEl().setWidth(this.adjustWidth('input', x));
14113 // this.trigger.setStyle('left', x+'px');
14118 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14121 getResizeEl : function(){
14122 return this.inputEl();
14126 getPositionEl : function(){
14127 return this.inputEl();
14131 alignErrorIcon : function(){
14132 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14136 initEvents : function(){
14140 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14141 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14142 if(!this.multiple && this.showToggleBtn){
14143 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14144 if(this.hideTrigger){
14145 this.trigger.setDisplayed(false);
14147 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14151 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14154 if(this.removable && !this.editable && !this.tickable){
14155 var close = this.closeTriggerEl();
14158 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14159 close.on('click', this.removeBtnClick, this, close);
14163 //this.trigger.addClassOnOver('x-form-trigger-over');
14164 //this.trigger.addClassOnClick('x-form-trigger-click');
14167 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14171 closeTriggerEl : function()
14173 var close = this.el.select('.roo-combo-removable-btn', true).first();
14174 return close ? close : false;
14177 removeBtnClick : function(e, h, el)
14179 e.preventDefault();
14181 if(this.fireEvent("remove", this) !== false){
14183 this.fireEvent("afterremove", this)
14187 createList : function()
14189 this.list = Roo.get(document.body).createChild({
14190 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14191 cls: 'typeahead typeahead-long dropdown-menu shadow',
14192 style: 'display:none'
14195 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14200 initTrigger : function(){
14205 onDestroy : function(){
14207 this.trigger.removeAllListeners();
14208 // this.trigger.remove();
14211 // this.wrap.remove();
14213 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14217 onFocus : function(){
14218 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14220 if(!this.mimicing){
14221 this.wrap.addClass('x-trigger-wrap-focus');
14222 this.mimicing = true;
14223 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14224 if(this.monitorTab){
14225 this.el.on("keydown", this.checkTab, this);
14232 checkTab : function(e){
14233 if(e.getKey() == e.TAB){
14234 this.triggerBlur();
14239 onBlur : function(){
14244 mimicBlur : function(e, t){
14246 if(!this.wrap.contains(t) && this.validateBlur()){
14247 this.triggerBlur();
14253 triggerBlur : function(){
14254 this.mimicing = false;
14255 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14256 if(this.monitorTab){
14257 this.el.un("keydown", this.checkTab, this);
14259 //this.wrap.removeClass('x-trigger-wrap-focus');
14260 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14264 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14265 validateBlur : function(e, t){
14270 onDisable : function(){
14271 this.inputEl().dom.disabled = true;
14272 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14274 // this.wrap.addClass('x-item-disabled');
14279 onEnable : function(){
14280 this.inputEl().dom.disabled = false;
14281 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14283 // this.el.removeClass('x-item-disabled');
14288 onShow : function(){
14289 var ae = this.getActionEl();
14292 ae.dom.style.display = '';
14293 ae.dom.style.visibility = 'visible';
14299 onHide : function(){
14300 var ae = this.getActionEl();
14301 ae.dom.style.display = 'none';
14305 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14306 * by an implementing function.
14308 * @param {EventObject} e
14310 onTriggerClick : Roo.emptyFn
14318 * @class Roo.bootstrap.form.CardUploader
14319 * @extends Roo.bootstrap.Button
14320 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14321 * @cfg {Number} errorTimeout default 3000
14322 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14323 * @cfg {Array} html The button text.
14327 * Create a new CardUploader
14328 * @param {Object} config The config object
14331 Roo.bootstrap.form.CardUploader = function(config){
14335 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14338 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14346 * When a image is clicked on - and needs to display a slideshow or similar..
14347 * @param {Roo.bootstrap.Card} this
14348 * @param {Object} The image information data
14354 * When a the download link is clicked
14355 * @param {Roo.bootstrap.Card} this
14356 * @param {Object} The image information data contains
14363 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14366 errorTimeout : 3000,
14370 fileCollection : false,
14373 getAutoCreate : function()
14377 cls :'form-group' ,
14382 //cls : 'input-group-addon',
14383 html : this.fieldLabel
14391 value : this.value,
14392 cls : 'd-none form-control'
14397 multiple : 'multiple',
14399 cls : 'd-none roo-card-upload-selector'
14403 cls : 'roo-card-uploader-button-container w-100 mb-2'
14406 cls : 'card-columns roo-card-uploader-container'
14416 getChildContainer : function() /// what children are added to.
14418 return this.containerEl;
14421 getButtonContainer : function() /// what children are added to.
14423 return this.el.select(".roo-card-uploader-button-container").first();
14426 initEvents : function()
14429 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14433 xns: Roo.bootstrap,
14436 container_method : 'getButtonContainer' ,
14437 html : this.html, // fix changable?
14440 'click' : function(btn, e) {
14449 this.urlAPI = (window.createObjectURL && window) ||
14450 (window.URL && URL.revokeObjectURL && URL) ||
14451 (window.webkitURL && webkitURL);
14456 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14458 this.selectorEl.on('change', this.onFileSelected, this);
14461 this.images.forEach(function(img) {
14464 this.images = false;
14466 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14472 onClick : function(e)
14474 e.preventDefault();
14476 this.selectorEl.dom.click();
14480 onFileSelected : function(e)
14482 e.preventDefault();
14484 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14488 Roo.each(this.selectorEl.dom.files, function(file){
14489 this.addFile(file);
14498 addFile : function(file)
14501 if(typeof(file) === 'string'){
14502 throw "Add file by name?"; // should not happen
14506 if(!file || !this.urlAPI){
14516 var url = _this.urlAPI.createObjectURL( file);
14519 id : Roo.bootstrap.form.CardUploader.ID--,
14520 is_uploaded : false,
14524 mimetype : file.type,
14532 * addCard - add an Attachment to the uploader
14533 * @param data - the data about the image to upload
14537 title : "Title of file",
14538 is_uploaded : false,
14539 src : "http://.....",
14540 srcfile : { the File upload object },
14541 mimetype : file.type,
14544 .. any other data...
14550 addCard : function (data)
14552 // hidden input element?
14553 // if the file is not an image...
14554 //then we need to use something other that and header_image
14559 xns : Roo.bootstrap,
14560 xtype : 'CardFooter',
14563 xns : Roo.bootstrap,
14569 xns : Roo.bootstrap,
14571 html : String.format("<small>{0}</small>", data.title),
14572 cls : 'col-10 text-left',
14577 click : function() {
14579 t.fireEvent( "download", t, data );
14585 xns : Roo.bootstrap,
14587 style: 'max-height: 28px; ',
14593 click : function() {
14594 t.removeCard(data.id)
14606 var cn = this.addxtype(
14609 xns : Roo.bootstrap,
14612 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14613 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14614 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14619 initEvents : function() {
14620 Roo.bootstrap.Card.prototype.initEvents.call(this);
14622 this.imgEl = this.el.select('.card-img-top').first();
14624 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14625 this.imgEl.set({ 'pointer' : 'cursor' });
14628 this.getCardFooter().addClass('p-1');
14635 // dont' really need ot update items.
14636 // this.items.push(cn);
14637 this.fileCollection.add(cn);
14639 if (!data.srcfile) {
14640 this.updateInput();
14645 var reader = new FileReader();
14646 reader.addEventListener("load", function() {
14647 data.srcdata = reader.result;
14650 reader.readAsDataURL(data.srcfile);
14655 removeCard : function(id)
14658 var card = this.fileCollection.get(id);
14659 card.data.is_deleted = 1;
14660 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14661 //this.fileCollection.remove(card);
14662 //this.items = this.items.filter(function(e) { return e != card });
14663 // dont' really need ot update items.
14664 card.el.dom.parentNode.removeChild(card.el.dom);
14665 this.updateInput();
14671 this.fileCollection.each(function(card) {
14672 if (card.el.dom && card.el.dom.parentNode) {
14673 card.el.dom.parentNode.removeChild(card.el.dom);
14676 this.fileCollection.clear();
14677 this.updateInput();
14680 updateInput : function()
14683 this.fileCollection.each(function(e) {
14687 this.inputEl().dom.value = JSON.stringify(data);
14697 Roo.bootstrap.form.CardUploader.ID = -1;/*
14699 * Ext JS Library 1.1.1
14700 * Copyright(c) 2006-2007, Ext JS, LLC.
14702 * Originally Released Under LGPL - original licence link has changed is not relivant.
14705 * <script type="text/javascript">
14710 * @class Roo.data.SortTypes
14712 * Defines the default sorting (casting?) comparison functions used when sorting data.
14714 Roo.data.SortTypes = {
14716 * Default sort that does nothing
14717 * @param {Mixed} s The value being converted
14718 * @return {Mixed} The comparison value
14720 none : function(s){
14725 * The regular expression used to strip tags
14729 stripTagsRE : /<\/?[^>]+>/gi,
14732 * Strips all HTML tags to sort on text only
14733 * @param {Mixed} s The value being converted
14734 * @return {String} The comparison value
14736 asText : function(s){
14737 return String(s).replace(this.stripTagsRE, "");
14741 * Strips all HTML tags to sort on text only - Case insensitive
14742 * @param {Mixed} s The value being converted
14743 * @return {String} The comparison value
14745 asUCText : function(s){
14746 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14750 * Case insensitive string
14751 * @param {Mixed} s The value being converted
14752 * @return {String} The comparison value
14754 asUCString : function(s) {
14755 return String(s).toUpperCase();
14760 * @param {Mixed} s The value being converted
14761 * @return {Number} The comparison value
14763 asDate : function(s) {
14767 if(s instanceof Date){
14768 return s.getTime();
14770 return Date.parse(String(s));
14775 * @param {Mixed} s The value being converted
14776 * @return {Float} The comparison value
14778 asFloat : function(s) {
14779 var val = parseFloat(String(s).replace(/,/g, ""));
14788 * @param {Mixed} s The value being converted
14789 * @return {Number} The comparison value
14791 asInt : function(s) {
14792 var val = parseInt(String(s).replace(/,/g, ""));
14800 * Ext JS Library 1.1.1
14801 * Copyright(c) 2006-2007, Ext JS, LLC.
14803 * Originally Released Under LGPL - original licence link has changed is not relivant.
14806 * <script type="text/javascript">
14810 * @class Roo.data.Record
14811 * Instances of this class encapsulate both record <em>definition</em> information, and record
14812 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14813 * to access Records cached in an {@link Roo.data.Store} object.<br>
14815 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14816 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14819 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14821 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14822 * {@link #create}. The parameters are the same.
14823 * @param {Array} data An associative Array of data values keyed by the field name.
14824 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14825 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14826 * not specified an integer id is generated.
14828 Roo.data.Record = function(data, id){
14829 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14834 * Generate a constructor for a specific record layout.
14835 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14836 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14837 * Each field definition object may contain the following properties: <ul>
14838 * <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,
14839 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14840 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14841 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14842 * is being used, then this is a string containing the javascript expression to reference the data relative to
14843 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14844 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14845 * this may be omitted.</p></li>
14846 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14847 * <ul><li>auto (Default, implies no conversion)</li>
14852 * <li>date</li></ul></p></li>
14853 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14854 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14855 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14856 * by the Reader into an object that will be stored in the Record. It is passed the
14857 * following parameters:<ul>
14858 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14860 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14862 * <br>usage:<br><pre><code>
14863 var TopicRecord = Roo.data.Record.create(
14864 {name: 'title', mapping: 'topic_title'},
14865 {name: 'author', mapping: 'username'},
14866 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14867 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14868 {name: 'lastPoster', mapping: 'user2'},
14869 {name: 'excerpt', mapping: 'post_text'}
14872 var myNewRecord = new TopicRecord({
14873 title: 'Do my job please',
14876 lastPost: new Date(),
14877 lastPoster: 'Animal',
14878 excerpt: 'No way dude!'
14880 myStore.add(myNewRecord);
14885 Roo.data.Record.create = function(o){
14886 var f = function(){
14887 f.superclass.constructor.apply(this, arguments);
14889 Roo.extend(f, Roo.data.Record);
14890 var p = f.prototype;
14891 p.fields = new Roo.util.MixedCollection(false, function(field){
14894 for(var i = 0, len = o.length; i < len; i++){
14895 p.fields.add(new Roo.data.Field(o[i]));
14897 f.getField = function(name){
14898 return p.fields.get(name);
14903 Roo.data.Record.AUTO_ID = 1000;
14904 Roo.data.Record.EDIT = 'edit';
14905 Roo.data.Record.REJECT = 'reject';
14906 Roo.data.Record.COMMIT = 'commit';
14908 Roo.data.Record.prototype = {
14910 * Readonly flag - true if this record has been modified.
14919 join : function(store){
14920 this.store = store;
14924 * Set the named field to the specified value.
14925 * @param {String} name The name of the field to set.
14926 * @param {Object} value The value to set the field to.
14928 set : function(name, value){
14929 if(this.data[name] == value){
14933 if(!this.modified){
14934 this.modified = {};
14936 if(typeof this.modified[name] == 'undefined'){
14937 this.modified[name] = this.data[name];
14939 this.data[name] = value;
14940 if(!this.editing && this.store){
14941 this.store.afterEdit(this);
14946 * Get the value of the named field.
14947 * @param {String} name The name of the field to get the value of.
14948 * @return {Object} The value of the field.
14950 get : function(name){
14951 return this.data[name];
14955 beginEdit : function(){
14956 this.editing = true;
14957 this.modified = {};
14961 cancelEdit : function(){
14962 this.editing = false;
14963 delete this.modified;
14967 endEdit : function(){
14968 this.editing = false;
14969 if(this.dirty && this.store){
14970 this.store.afterEdit(this);
14975 * Usually called by the {@link Roo.data.Store} which owns the Record.
14976 * Rejects all changes made to the Record since either creation, or the last commit operation.
14977 * Modified fields are reverted to their original values.
14979 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14980 * of reject operations.
14982 reject : function(){
14983 var m = this.modified;
14985 if(typeof m[n] != "function"){
14986 this.data[n] = m[n];
14989 this.dirty = false;
14990 delete this.modified;
14991 this.editing = false;
14993 this.store.afterReject(this);
14998 * Usually called by the {@link Roo.data.Store} which owns the Record.
14999 * Commits all changes made to the Record since either creation, or the last commit operation.
15001 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15002 * of commit operations.
15004 commit : function(){
15005 this.dirty = false;
15006 delete this.modified;
15007 this.editing = false;
15009 this.store.afterCommit(this);
15014 hasError : function(){
15015 return this.error != null;
15019 clearError : function(){
15024 * Creates a copy of this record.
15025 * @param {String} id (optional) A new record id if you don't want to use this record's id
15028 copy : function(newId) {
15029 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15033 * Ext JS Library 1.1.1
15034 * Copyright(c) 2006-2007, Ext JS, LLC.
15036 * Originally Released Under LGPL - original licence link has changed is not relivant.
15039 * <script type="text/javascript">
15045 * @class Roo.data.Store
15046 * @extends Roo.util.Observable
15047 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15048 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15050 * 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
15051 * has no knowledge of the format of the data returned by the Proxy.<br>
15053 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15054 * instances from the data object. These records are cached and made available through accessor functions.
15056 * Creates a new Store.
15057 * @param {Object} config A config object containing the objects needed for the Store to access data,
15058 * and read the data into Records.
15060 Roo.data.Store = function(config){
15061 this.data = new Roo.util.MixedCollection(false);
15062 this.data.getKey = function(o){
15065 this.baseParams = {};
15067 this.paramNames = {
15072 "multisort" : "_multisort"
15075 if(config && config.data){
15076 this.inlineData = config.data;
15077 delete config.data;
15080 Roo.apply(this, config);
15082 if(this.reader){ // reader passed
15083 this.reader = Roo.factory(this.reader, Roo.data);
15084 this.reader.xmodule = this.xmodule || false;
15085 if(!this.recordType){
15086 this.recordType = this.reader.recordType;
15088 if(this.reader.onMetaChange){
15089 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15093 if(this.recordType){
15094 this.fields = this.recordType.prototype.fields;
15096 this.modified = [];
15100 * @event datachanged
15101 * Fires when the data cache has changed, and a widget which is using this Store
15102 * as a Record cache should refresh its view.
15103 * @param {Store} this
15105 datachanged : true,
15107 * @event metachange
15108 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15109 * @param {Store} this
15110 * @param {Object} meta The JSON metadata
15115 * Fires when Records have been added to the Store
15116 * @param {Store} this
15117 * @param {Roo.data.Record[]} records The array of Records added
15118 * @param {Number} index The index at which the record(s) were added
15123 * Fires when a Record has been removed from the Store
15124 * @param {Store} this
15125 * @param {Roo.data.Record} record The Record that was removed
15126 * @param {Number} index The index at which the record was removed
15131 * Fires when a Record has been updated
15132 * @param {Store} this
15133 * @param {Roo.data.Record} record The Record that was updated
15134 * @param {String} operation The update operation being performed. Value may be one of:
15136 Roo.data.Record.EDIT
15137 Roo.data.Record.REJECT
15138 Roo.data.Record.COMMIT
15144 * Fires when the data cache has been cleared.
15145 * @param {Store} this
15149 * @event beforeload
15150 * Fires before a request is made for a new data object. If the beforeload handler returns false
15151 * the load action will be canceled.
15152 * @param {Store} this
15153 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15157 * @event beforeloadadd
15158 * Fires after a new set of Records has been loaded.
15159 * @param {Store} this
15160 * @param {Roo.data.Record[]} records The Records that were loaded
15161 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15163 beforeloadadd : true,
15166 * Fires after a new set of Records has been loaded, before they are added to the store.
15167 * @param {Store} this
15168 * @param {Roo.data.Record[]} records The Records that were loaded
15169 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15170 * @params {Object} return from reader
15174 * @event loadexception
15175 * Fires if an exception occurs in the Proxy during loading.
15176 * Called with the signature of the Proxy's "loadexception" event.
15177 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15180 * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15181 * @param {Object} opts - load Options
15182 * @param {Object} jsonData from your request (normally this contains the Exception)
15184 loadexception : true
15188 this.proxy = Roo.factory(this.proxy, Roo.data);
15189 this.proxy.xmodule = this.xmodule || false;
15190 this.relayEvents(this.proxy, ["loadexception"]);
15192 this.sortToggle = {};
15193 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15195 Roo.data.Store.superclass.constructor.call(this);
15197 if(this.inlineData){
15198 this.loadData(this.inlineData);
15199 delete this.inlineData;
15203 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15205 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15206 * without a remote query - used by combo/forms at present.
15210 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15213 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15216 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15217 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15220 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15221 * on any HTTP request
15224 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15227 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15231 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15232 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15234 remoteSort : false,
15237 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15238 * loaded or when a record is removed. (defaults to false).
15240 pruneModifiedRecords : false,
15243 lastOptions : null,
15246 * Add Records to the Store and fires the add event.
15247 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15249 add : function(records){
15250 records = [].concat(records);
15251 for(var i = 0, len = records.length; i < len; i++){
15252 records[i].join(this);
15254 var index = this.data.length;
15255 this.data.addAll(records);
15256 this.fireEvent("add", this, records, index);
15260 * Remove a Record from the Store and fires the remove event.
15261 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15263 remove : function(record){
15264 var index = this.data.indexOf(record);
15265 this.data.removeAt(index);
15267 if(this.pruneModifiedRecords){
15268 this.modified.remove(record);
15270 this.fireEvent("remove", this, record, index);
15274 * Remove all Records from the Store and fires the clear event.
15276 removeAll : function(){
15278 if(this.pruneModifiedRecords){
15279 this.modified = [];
15281 this.fireEvent("clear", this);
15285 * Inserts Records to the Store at the given index and fires the add event.
15286 * @param {Number} index The start index at which to insert the passed Records.
15287 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15289 insert : function(index, records){
15290 records = [].concat(records);
15291 for(var i = 0, len = records.length; i < len; i++){
15292 this.data.insert(index, records[i]);
15293 records[i].join(this);
15295 this.fireEvent("add", this, records, index);
15299 * Get the index within the cache of the passed Record.
15300 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15301 * @return {Number} The index of the passed Record. Returns -1 if not found.
15303 indexOf : function(record){
15304 return this.data.indexOf(record);
15308 * Get the index within the cache of the Record with the passed id.
15309 * @param {String} id The id of the Record to find.
15310 * @return {Number} The index of the Record. Returns -1 if not found.
15312 indexOfId : function(id){
15313 return this.data.indexOfKey(id);
15317 * Get the Record with the specified id.
15318 * @param {String} id The id of the Record to find.
15319 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15321 getById : function(id){
15322 return this.data.key(id);
15326 * Get the Record at the specified index.
15327 * @param {Number} index The index of the Record to find.
15328 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15330 getAt : function(index){
15331 return this.data.itemAt(index);
15335 * Returns a range of Records between specified indices.
15336 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15337 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15338 * @return {Roo.data.Record[]} An array of Records
15340 getRange : function(start, end){
15341 return this.data.getRange(start, end);
15345 storeOptions : function(o){
15346 o = Roo.apply({}, o);
15349 this.lastOptions = o;
15353 * Loads the Record cache from the configured Proxy using the configured Reader.
15355 * If using remote paging, then the first load call must specify the <em>start</em>
15356 * and <em>limit</em> properties in the options.params property to establish the initial
15357 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15359 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15360 * and this call will return before the new data has been loaded. Perform any post-processing
15361 * in a callback function, or in a "load" event handler.</strong>
15363 * @param {Object} options An object containing properties which control loading options:<ul>
15364 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15365 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15368 data : data, // array of key=>value data like JsonReader
15369 total : data.length,
15375 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15376 * passed the following arguments:<ul>
15377 * <li>r : Roo.data.Record[]</li>
15378 * <li>options: Options object from the load call</li>
15379 * <li>success: Boolean success indicator</li></ul></li>
15380 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15381 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15384 load : function(options){
15385 options = options || {};
15386 if(this.fireEvent("beforeload", this, options) !== false){
15387 this.storeOptions(options);
15388 var p = Roo.apply(options.params || {}, this.baseParams);
15389 // if meta was not loaded from remote source.. try requesting it.
15390 if (!this.reader.metaFromRemote) {
15391 p._requestMeta = 1;
15393 if(this.sortInfo && this.remoteSort){
15394 var pn = this.paramNames;
15395 p[pn["sort"]] = this.sortInfo.field;
15396 p[pn["dir"]] = this.sortInfo.direction;
15398 if (this.multiSort) {
15399 var pn = this.paramNames;
15400 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15403 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15408 * Reloads the Record cache from the configured Proxy using the configured Reader and
15409 * the options from the last load operation performed.
15410 * @param {Object} options (optional) An object containing properties which may override the options
15411 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15412 * the most recently used options are reused).
15414 reload : function(options){
15415 this.load(Roo.applyIf(options||{}, this.lastOptions));
15419 // Called as a callback by the Reader during a load operation.
15420 loadRecords : function(o, options, success){
15423 if(success !== false){
15424 this.fireEvent("load", this, [], options, o);
15426 if(options.callback){
15427 options.callback.call(options.scope || this, [], options, false);
15431 // if data returned failure - throw an exception.
15432 if (o.success === false) {
15433 // show a message if no listener is registered.
15434 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15435 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15437 // loadmask wil be hooked into this..
15438 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15441 var r = o.records, t = o.totalRecords || r.length;
15443 this.fireEvent("beforeloadadd", this, r, options, o);
15445 if(!options || options.add !== true){
15446 if(this.pruneModifiedRecords){
15447 this.modified = [];
15449 for(var i = 0, len = r.length; i < len; i++){
15453 this.data = this.snapshot;
15454 delete this.snapshot;
15457 this.data.addAll(r);
15458 this.totalLength = t;
15460 this.fireEvent("datachanged", this);
15462 this.totalLength = Math.max(t, this.data.length+r.length);
15466 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15468 var e = new Roo.data.Record({});
15470 e.set(this.parent.displayField, this.parent.emptyTitle);
15471 e.set(this.parent.valueField, '');
15476 this.fireEvent("load", this, r, options, o);
15477 if(options.callback){
15478 options.callback.call(options.scope || this, r, options, true);
15484 * Loads data from a passed data block. A Reader which understands the format of the data
15485 * must have been configured in the constructor.
15486 * @param {Object} data The data block from which to read the Records. The format of the data expected
15487 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15488 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15490 loadData : function(o, append){
15491 var r = this.reader.readRecords(o);
15492 this.loadRecords(r, {add: append}, true);
15496 * using 'cn' the nested child reader read the child array into it's child stores.
15497 * @param {Object} rec The record with a 'children array
15499 loadDataFromChildren : function(rec)
15501 this.loadData(this.reader.toLoadData(rec));
15506 * Gets the number of cached records.
15508 * <em>If using paging, this may not be the total size of the dataset. If the data object
15509 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15510 * the data set size</em>
15512 getCount : function(){
15513 return this.data.length || 0;
15517 * Gets the total number of records in the dataset as returned by the server.
15519 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15520 * the dataset size</em>
15522 getTotalCount : function(){
15523 return this.totalLength || 0;
15527 * Returns the sort state of the Store as an object with two properties:
15529 field {String} The name of the field by which the Records are sorted
15530 direction {String} The sort order, "ASC" or "DESC"
15533 getSortState : function(){
15534 return this.sortInfo;
15538 applySort : function(){
15539 if(this.sortInfo && !this.remoteSort){
15540 var s = this.sortInfo, f = s.field;
15541 var st = this.fields.get(f).sortType;
15542 var fn = function(r1, r2){
15543 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15544 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15546 this.data.sort(s.direction, fn);
15547 if(this.snapshot && this.snapshot != this.data){
15548 this.snapshot.sort(s.direction, fn);
15554 * Sets the default sort column and order to be used by the next load operation.
15555 * @param {String} fieldName The name of the field to sort by.
15556 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15558 setDefaultSort : function(field, dir){
15559 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15563 * Sort the Records.
15564 * If remote sorting is used, the sort is performed on the server, and the cache is
15565 * reloaded. If local sorting is used, the cache is sorted internally.
15566 * @param {String} fieldName The name of the field to sort by.
15567 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15569 sort : function(fieldName, dir){
15570 var f = this.fields.get(fieldName);
15572 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15574 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15575 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15580 this.sortToggle[f.name] = dir;
15581 this.sortInfo = {field: f.name, direction: dir};
15582 if(!this.remoteSort){
15584 this.fireEvent("datachanged", this);
15586 this.load(this.lastOptions);
15591 * Calls the specified function for each of the Records in the cache.
15592 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15593 * Returning <em>false</em> aborts and exits the iteration.
15594 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15596 each : function(fn, scope){
15597 this.data.each(fn, scope);
15601 * Gets all records modified since the last commit. Modified records are persisted across load operations
15602 * (e.g., during paging).
15603 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15605 getModifiedRecords : function(){
15606 return this.modified;
15610 createFilterFn : function(property, value, anyMatch){
15611 if(!value.exec){ // not a regex
15612 value = String(value);
15613 if(value.length == 0){
15616 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15618 return function(r){
15619 return value.test(r.data[property]);
15624 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15625 * @param {String} property A field on your records
15626 * @param {Number} start The record index to start at (defaults to 0)
15627 * @param {Number} end The last record index to include (defaults to length - 1)
15628 * @return {Number} The sum
15630 sum : function(property, start, end){
15631 var rs = this.data.items, v = 0;
15632 start = start || 0;
15633 end = (end || end === 0) ? end : rs.length-1;
15635 for(var i = start; i <= end; i++){
15636 v += (rs[i].data[property] || 0);
15642 * Filter the records by a specified property.
15643 * @param {String} field A field on your records
15644 * @param {String/RegExp} value Either a string that the field
15645 * should start with or a RegExp to test against the field
15646 * @param {Boolean} anyMatch True to match any part not just the beginning
15648 filter : function(property, value, anyMatch){
15649 var fn = this.createFilterFn(property, value, anyMatch);
15650 return fn ? this.filterBy(fn) : this.clearFilter();
15654 * Filter by a function. The specified function will be called with each
15655 * record in this data source. If the function returns true the record is included,
15656 * otherwise it is filtered.
15657 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15658 * @param {Object} scope (optional) The scope of the function (defaults to this)
15660 filterBy : function(fn, scope){
15661 this.snapshot = this.snapshot || this.data;
15662 this.data = this.queryBy(fn, scope||this);
15663 this.fireEvent("datachanged", this);
15667 * Query the records by a specified property.
15668 * @param {String} field A field on your records
15669 * @param {String/RegExp} value Either a string that the field
15670 * should start with or a RegExp to test against the field
15671 * @param {Boolean} anyMatch True to match any part not just the beginning
15672 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15674 query : function(property, value, anyMatch){
15675 var fn = this.createFilterFn(property, value, anyMatch);
15676 return fn ? this.queryBy(fn) : this.data.clone();
15680 * Query by a function. The specified function will be called with each
15681 * record in this data source. If the function returns true the record is included
15683 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15684 * @param {Object} scope (optional) The scope of the function (defaults to this)
15685 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15687 queryBy : function(fn, scope){
15688 var data = this.snapshot || this.data;
15689 return data.filterBy(fn, scope||this);
15693 * Collects unique values for a particular dataIndex from this store.
15694 * @param {String} dataIndex The property to collect
15695 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15696 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15697 * @return {Array} An array of the unique values
15699 collect : function(dataIndex, allowNull, bypassFilter){
15700 var d = (bypassFilter === true && this.snapshot) ?
15701 this.snapshot.items : this.data.items;
15702 var v, sv, r = [], l = {};
15703 for(var i = 0, len = d.length; i < len; i++){
15704 v = d[i].data[dataIndex];
15706 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15715 * Revert to a view of the Record cache with no filtering applied.
15716 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15718 clearFilter : function(suppressEvent){
15719 if(this.snapshot && this.snapshot != this.data){
15720 this.data = this.snapshot;
15721 delete this.snapshot;
15722 if(suppressEvent !== true){
15723 this.fireEvent("datachanged", this);
15729 afterEdit : function(record){
15730 if(this.modified.indexOf(record) == -1){
15731 this.modified.push(record);
15733 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15737 afterReject : function(record){
15738 this.modified.remove(record);
15739 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15743 afterCommit : function(record){
15744 this.modified.remove(record);
15745 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15749 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15750 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15752 commitChanges : function(){
15753 var m = this.modified.slice(0);
15754 this.modified = [];
15755 for(var i = 0, len = m.length; i < len; i++){
15761 * Cancel outstanding changes on all changed records.
15763 rejectChanges : function(){
15764 var m = this.modified.slice(0);
15765 this.modified = [];
15766 for(var i = 0, len = m.length; i < len; i++){
15771 onMetaChange : function(meta, rtype, o){
15772 this.recordType = rtype;
15773 this.fields = rtype.prototype.fields;
15774 delete this.snapshot;
15775 this.sortInfo = meta.sortInfo || this.sortInfo;
15776 this.modified = [];
15777 this.fireEvent('metachange', this, this.reader.meta);
15780 moveIndex : function(data, type)
15782 var index = this.indexOf(data);
15784 var newIndex = index + type;
15788 this.insert(newIndex, data);
15793 * Ext JS Library 1.1.1
15794 * Copyright(c) 2006-2007, Ext JS, LLC.
15796 * Originally Released Under LGPL - original licence link has changed is not relivant.
15799 * <script type="text/javascript">
15803 * @class Roo.data.SimpleStore
15804 * @extends Roo.data.Store
15805 * Small helper class to make creating Stores from Array data easier.
15806 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15807 * @cfg {Array} fields An array of field definition objects, or field name strings.
15808 * @cfg {Object} an existing reader (eg. copied from another store)
15809 * @cfg {Array} data The multi-dimensional array of data
15810 * @cfg {Roo.data.DataProxy} proxy [not-required]
15811 * @cfg {Roo.data.Reader} reader [not-required]
15813 * @param {Object} config
15815 Roo.data.SimpleStore = function(config)
15817 Roo.data.SimpleStore.superclass.constructor.call(this, {
15819 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15822 Roo.data.Record.create(config.fields)
15824 proxy : new Roo.data.MemoryProxy(config.data)
15828 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15830 * Ext JS Library 1.1.1
15831 * Copyright(c) 2006-2007, Ext JS, LLC.
15833 * Originally Released Under LGPL - original licence link has changed is not relivant.
15836 * <script type="text/javascript">
15841 * @extends Roo.data.Store
15842 * @class Roo.data.JsonStore
15843 * Small helper class to make creating Stores for JSON data easier. <br/>
15845 var store = new Roo.data.JsonStore({
15846 url: 'get-images.php',
15848 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15851 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15852 * JsonReader and HttpProxy (unless inline data is provided).</b>
15853 * @cfg {Array} fields An array of field definition objects, or field name strings.
15855 * @param {Object} config
15857 Roo.data.JsonStore = function(c){
15858 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15859 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15860 reader: new Roo.data.JsonReader(c, c.fields)
15863 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15865 * Ext JS Library 1.1.1
15866 * Copyright(c) 2006-2007, Ext JS, LLC.
15868 * Originally Released Under LGPL - original licence link has changed is not relivant.
15871 * <script type="text/javascript">
15875 Roo.data.Field = function(config){
15876 if(typeof config == "string"){
15877 config = {name: config};
15879 Roo.apply(this, config);
15882 this.type = "auto";
15885 var st = Roo.data.SortTypes;
15886 // named sortTypes are supported, here we look them up
15887 if(typeof this.sortType == "string"){
15888 this.sortType = st[this.sortType];
15891 // set default sortType for strings and dates
15892 if(!this.sortType){
15895 this.sortType = st.asUCString;
15898 this.sortType = st.asDate;
15901 this.sortType = st.none;
15906 var stripRe = /[\$,%]/g;
15908 // prebuilt conversion function for this field, instead of
15909 // switching every time we're reading a value
15911 var cv, dateFormat = this.dateFormat;
15916 cv = function(v){ return v; };
15919 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15923 return v !== undefined && v !== null && v !== '' ?
15924 parseInt(String(v).replace(stripRe, ""), 10) : '';
15929 return v !== undefined && v !== null && v !== '' ?
15930 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15935 cv = function(v){ return v === true || v === "true" || v == 1; };
15942 if(v instanceof Date){
15946 if(dateFormat == "timestamp"){
15947 return new Date(v*1000);
15949 return Date.parseDate(v, dateFormat);
15951 var parsed = Date.parse(v);
15952 return parsed ? new Date(parsed) : null;
15961 Roo.data.Field.prototype = {
15969 * Ext JS Library 1.1.1
15970 * Copyright(c) 2006-2007, Ext JS, LLC.
15972 * Originally Released Under LGPL - original licence link has changed is not relivant.
15975 * <script type="text/javascript">
15978 // Base class for reading structured data from a data source. This class is intended to be
15979 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15982 * @class Roo.data.DataReader
15984 * Base class for reading structured data from a data source. This class is intended to be
15985 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15988 Roo.data.DataReader = function(meta, recordType){
15992 this.recordType = recordType instanceof Array ?
15993 Roo.data.Record.create(recordType) : recordType;
15996 Roo.data.DataReader.prototype = {
15999 readerType : 'Data',
16001 * Create an empty record
16002 * @param {Object} data (optional) - overlay some values
16003 * @return {Roo.data.Record} record created.
16005 newRow : function(d) {
16007 this.recordType.prototype.fields.each(function(c) {
16009 case 'int' : da[c.name] = 0; break;
16010 case 'date' : da[c.name] = new Date(); break;
16011 case 'float' : da[c.name] = 0.0; break;
16012 case 'boolean' : da[c.name] = false; break;
16013 default : da[c.name] = ""; break;
16017 return new this.recordType(Roo.apply(da, d));
16023 * Ext JS Library 1.1.1
16024 * Copyright(c) 2006-2007, Ext JS, LLC.
16026 * Originally Released Under LGPL - original licence link has changed is not relivant.
16029 * <script type="text/javascript">
16033 * @class Roo.data.DataProxy
16034 * @extends Roo.util.Observable
16036 * This class is an abstract base class for implementations which provide retrieval of
16037 * unformatted data objects.<br>
16039 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16040 * (of the appropriate type which knows how to parse the data object) to provide a block of
16041 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16043 * Custom implementations must implement the load method as described in
16044 * {@link Roo.data.HttpProxy#load}.
16046 Roo.data.DataProxy = function(){
16049 * @event beforeload
16050 * Fires before a network request is made to retrieve a data object.
16051 * @param {Object} This DataProxy object.
16052 * @param {Object} params The params parameter to the load function.
16057 * Fires before the load method's callback is called.
16058 * @param {Object} This DataProxy object.
16059 * @param {Object} o The data object.
16060 * @param {Object} arg The callback argument object passed to the load function.
16064 * @event loadexception
16065 * Fires if an Exception occurs during data retrieval.
16066 * @param {Object} This DataProxy object.
16067 * @param {Object} o The data object.
16068 * @param {Object} arg The callback argument object passed to the load function.
16069 * @param {Object} e The Exception.
16071 loadexception : true
16073 Roo.data.DataProxy.superclass.constructor.call(this);
16076 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16079 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16083 * Ext JS Library 1.1.1
16084 * Copyright(c) 2006-2007, Ext JS, LLC.
16086 * Originally Released Under LGPL - original licence link has changed is not relivant.
16089 * <script type="text/javascript">
16092 * @class Roo.data.MemoryProxy
16093 * @extends Roo.data.DataProxy
16094 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16095 * to the Reader when its load method is called.
16097 * @param {Object} config A config object containing the objects needed for the Store to access data,
16099 Roo.data.MemoryProxy = function(config){
16101 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16102 data = config.data;
16104 Roo.data.MemoryProxy.superclass.constructor.call(this);
16108 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16111 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16114 * Load data from the requested source (in this case an in-memory
16115 * data object passed to the constructor), read the data object into
16116 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16117 * process that block using the passed callback.
16118 * @param {Object} params This parameter is not used by the MemoryProxy class.
16119 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16120 * object into a block of Roo.data.Records.
16121 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16122 * The function must be passed <ul>
16123 * <li>The Record block object</li>
16124 * <li>The "arg" argument from the load function</li>
16125 * <li>A boolean success indicator</li>
16127 * @param {Object} scope The scope in which to call the callback
16128 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16130 load : function(params, reader, callback, scope, arg){
16131 params = params || {};
16134 result = reader.readRecords(params.data ? params.data :this.data);
16136 this.fireEvent("loadexception", this, arg, null, e);
16137 callback.call(scope, null, arg, false);
16140 callback.call(scope, result, arg, true);
16144 update : function(params, records){
16149 * Ext JS Library 1.1.1
16150 * Copyright(c) 2006-2007, Ext JS, LLC.
16152 * Originally Released Under LGPL - original licence link has changed is not relivant.
16155 * <script type="text/javascript">
16158 * @class Roo.data.HttpProxy
16159 * @extends Roo.data.DataProxy
16160 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16161 * configured to reference a certain URL.<br><br>
16163 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16164 * from which the running page was served.<br><br>
16166 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16168 * Be aware that to enable the browser to parse an XML document, the server must set
16169 * the Content-Type header in the HTTP response to "text/xml".
16171 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16172 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16173 * will be used to make the request.
16175 Roo.data.HttpProxy = function(conn){
16176 Roo.data.HttpProxy.superclass.constructor.call(this);
16177 // is conn a conn config or a real conn?
16179 this.useAjax = !conn || !conn.events;
16183 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16184 // thse are take from connection...
16187 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
16190 * @cfg {Object} extraParams An object containing properties which are used as
16191 * extra parameters to each request made by this object. (defaults to undefined)
16194 * @cfg {Object} defaultHeaders An object containing request headers which are added
16195 * to each request made by this object. (defaults to undefined)
16198 * @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)
16201 * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16204 * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16210 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16214 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16215 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16216 * a finer-grained basis than the DataProxy events.
16218 getConnection : function(){
16219 return this.useAjax ? Roo.Ajax : this.conn;
16223 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16224 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16225 * process that block using the passed callback.
16226 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16227 * for the request to the remote server.
16228 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16229 * object into a block of Roo.data.Records.
16230 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16231 * The function must be passed <ul>
16232 * <li>The Record block object</li>
16233 * <li>The "arg" argument from the load function</li>
16234 * <li>A boolean success indicator</li>
16236 * @param {Object} scope The scope in which to call the callback
16237 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16239 load : function(params, reader, callback, scope, arg){
16240 if(this.fireEvent("beforeload", this, params) !== false){
16242 params : params || {},
16244 callback : callback,
16249 callback : this.loadResponse,
16253 Roo.applyIf(o, this.conn);
16254 if(this.activeRequest){
16255 Roo.Ajax.abort(this.activeRequest);
16257 this.activeRequest = Roo.Ajax.request(o);
16259 this.conn.request(o);
16262 callback.call(scope||this, null, arg, false);
16267 loadResponse : function(o, success, response){
16268 delete this.activeRequest;
16270 this.fireEvent("loadexception", this, o, response);
16271 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16276 result = o.reader.read(response);
16279 o.raw = { errorMsg : response.responseText };
16280 this.fireEvent("loadexception", this, o, response, e);
16281 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16285 this.fireEvent("load", this, o, o.request.arg);
16286 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16290 update : function(dataSet){
16295 updateResponse : function(dataSet){
16300 * Ext JS Library 1.1.1
16301 * Copyright(c) 2006-2007, Ext JS, LLC.
16303 * Originally Released Under LGPL - original licence link has changed is not relivant.
16306 * <script type="text/javascript">
16310 * @class Roo.data.ScriptTagProxy
16311 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16312 * other than the originating domain of the running page.<br><br>
16314 * <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
16315 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16317 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16318 * source code that is used as the source inside a <script> tag.<br><br>
16320 * In order for the browser to process the returned data, the server must wrap the data object
16321 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16322 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16323 * depending on whether the callback name was passed:
16326 boolean scriptTag = false;
16327 String cb = request.getParameter("callback");
16330 response.setContentType("text/javascript");
16332 response.setContentType("application/x-json");
16334 Writer out = response.getWriter();
16336 out.write(cb + "(");
16338 out.print(dataBlock.toJsonString());
16345 * @param {Object} config A configuration object.
16347 Roo.data.ScriptTagProxy = function(config){
16348 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16349 Roo.apply(this, config);
16350 this.head = document.getElementsByTagName("head")[0];
16353 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16355 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16357 * @cfg {String} url The URL from which to request the data object.
16360 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16364 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16365 * the server the name of the callback function set up by the load call to process the returned data object.
16366 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16367 * javascript output which calls this named function passing the data object as its only parameter.
16369 callbackParam : "callback",
16371 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16372 * name to the request.
16377 * Load data from the configured URL, read the data object into
16378 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16379 * process that block using the passed callback.
16380 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16381 * for the request to the remote server.
16382 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16383 * object into a block of Roo.data.Records.
16384 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16385 * The function must be passed <ul>
16386 * <li>The Record block object</li>
16387 * <li>The "arg" argument from the load function</li>
16388 * <li>A boolean success indicator</li>
16390 * @param {Object} scope The scope in which to call the callback
16391 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16393 load : function(params, reader, callback, scope, arg){
16394 if(this.fireEvent("beforeload", this, params) !== false){
16396 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16398 var url = this.url;
16399 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16401 url += "&_dc=" + (new Date().getTime());
16403 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16406 cb : "stcCallback"+transId,
16407 scriptId : "stcScript"+transId,
16411 callback : callback,
16417 window[trans.cb] = function(o){
16418 conn.handleResponse(o, trans);
16421 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16423 if(this.autoAbort !== false){
16427 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16429 var script = document.createElement("script");
16430 script.setAttribute("src", url);
16431 script.setAttribute("type", "text/javascript");
16432 script.setAttribute("id", trans.scriptId);
16433 this.head.appendChild(script);
16435 this.trans = trans;
16437 callback.call(scope||this, null, arg, false);
16442 isLoading : function(){
16443 return this.trans ? true : false;
16447 * Abort the current server request.
16449 abort : function(){
16450 if(this.isLoading()){
16451 this.destroyTrans(this.trans);
16456 destroyTrans : function(trans, isLoaded){
16457 this.head.removeChild(document.getElementById(trans.scriptId));
16458 clearTimeout(trans.timeoutId);
16460 window[trans.cb] = undefined;
16462 delete window[trans.cb];
16465 // if hasn't been loaded, wait for load to remove it to prevent script error
16466 window[trans.cb] = function(){
16467 window[trans.cb] = undefined;
16469 delete window[trans.cb];
16476 handleResponse : function(o, trans){
16477 this.trans = false;
16478 this.destroyTrans(trans, true);
16481 result = trans.reader.readRecords(o);
16483 this.fireEvent("loadexception", this, o, trans.arg, e);
16484 trans.callback.call(trans.scope||window, null, trans.arg, false);
16487 this.fireEvent("load", this, o, trans.arg);
16488 trans.callback.call(trans.scope||window, result, trans.arg, true);
16492 handleFailure : function(trans){
16493 this.trans = false;
16494 this.destroyTrans(trans, false);
16495 this.fireEvent("loadexception", this, null, trans.arg);
16496 trans.callback.call(trans.scope||window, null, trans.arg, false);
16500 * Ext JS Library 1.1.1
16501 * Copyright(c) 2006-2007, Ext JS, LLC.
16503 * Originally Released Under LGPL - original licence link has changed is not relivant.
16506 * <script type="text/javascript">
16510 * @class Roo.data.JsonReader
16511 * @extends Roo.data.DataReader
16512 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16513 * based on mappings in a provided Roo.data.Record constructor.
16515 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16516 * in the reply previously.
16521 var RecordDef = Roo.data.Record.create([
16522 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16523 {name: 'occupation'} // This field will use "occupation" as the mapping.
16525 var myReader = new Roo.data.JsonReader({
16526 totalProperty: "results", // The property which contains the total dataset size (optional)
16527 root: "rows", // The property which contains an Array of row objects
16528 id: "id" // The property within each row object that provides an ID for the record (optional)
16532 * This would consume a JSON file like this:
16534 { 'results': 2, 'rows': [
16535 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16536 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16539 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16540 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16541 * paged from the remote server.
16542 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16543 * @cfg {String} root name of the property which contains the Array of row objects.
16544 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16545 * @cfg {Array} fields Array of field definition objects
16547 * Create a new JsonReader
16548 * @param {Object} meta Metadata configuration options
16549 * @param {Object} recordType Either an Array of field definition objects,
16550 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16552 Roo.data.JsonReader = function(meta, recordType){
16555 // set some defaults:
16556 Roo.applyIf(meta, {
16557 totalProperty: 'total',
16558 successProperty : 'success',
16563 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16565 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16567 readerType : 'Json',
16570 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16571 * Used by Store query builder to append _requestMeta to params.
16574 metaFromRemote : false,
16576 * This method is only used by a DataProxy which has retrieved data from a remote server.
16577 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16578 * @return {Object} data A data block which is used by an Roo.data.Store object as
16579 * a cache of Roo.data.Records.
16581 read : function(response){
16582 var json = response.responseText;
16584 var o = /* eval:var:o */ eval("("+json+")");
16586 throw {message: "JsonReader.read: Json object not found"};
16592 this.metaFromRemote = true;
16593 this.meta = o.metaData;
16594 this.recordType = Roo.data.Record.create(o.metaData.fields);
16595 this.onMetaChange(this.meta, this.recordType, o);
16597 return this.readRecords(o);
16600 // private function a store will implement
16601 onMetaChange : function(meta, recordType, o){
16608 simpleAccess: function(obj, subsc) {
16615 getJsonAccessor: function(){
16617 return function(expr) {
16619 return(re.test(expr))
16620 ? new Function("obj", "return obj." + expr)
16625 return Roo.emptyFn;
16630 * Create a data block containing Roo.data.Records from an XML document.
16631 * @param {Object} o An object which contains an Array of row objects in the property specified
16632 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16633 * which contains the total size of the dataset.
16634 * @return {Object} data A data block which is used by an Roo.data.Store object as
16635 * a cache of Roo.data.Records.
16637 readRecords : function(o){
16639 * After any data loads, the raw JSON data is available for further custom processing.
16643 var s = this.meta, Record = this.recordType,
16644 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16646 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16648 if(s.totalProperty) {
16649 this.getTotal = this.getJsonAccessor(s.totalProperty);
16651 if(s.successProperty) {
16652 this.getSuccess = this.getJsonAccessor(s.successProperty);
16654 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16656 var g = this.getJsonAccessor(s.id);
16657 this.getId = function(rec) {
16659 return (r === undefined || r === "") ? null : r;
16662 this.getId = function(){return null;};
16665 for(var jj = 0; jj < fl; jj++){
16667 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16668 this.ef[jj] = this.getJsonAccessor(map);
16672 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16673 if(s.totalProperty){
16674 var vt = parseInt(this.getTotal(o), 10);
16679 if(s.successProperty){
16680 var vs = this.getSuccess(o);
16681 if(vs === false || vs === 'false'){
16686 for(var i = 0; i < c; i++){
16689 var id = this.getId(n);
16690 for(var j = 0; j < fl; j++){
16692 var v = this.ef[j](n);
16694 Roo.log('missing convert for ' + f.name);
16698 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16702 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16708 var record = new Record(values, id);
16710 records[i] = record;
16716 totalRecords : totalRecords
16719 // used when loading children.. @see loadDataFromChildren
16720 toLoadData: function(rec)
16722 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16723 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16724 return { data : data, total : data.length };
16729 * Ext JS Library 1.1.1
16730 * Copyright(c) 2006-2007, Ext JS, LLC.
16732 * Originally Released Under LGPL - original licence link has changed is not relivant.
16735 * <script type="text/javascript">
16739 * @class Roo.data.ArrayReader
16740 * @extends Roo.data.DataReader
16741 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16742 * Each element of that Array represents a row of data fields. The
16743 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16744 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16748 var RecordDef = Roo.data.Record.create([
16749 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16750 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16752 var myReader = new Roo.data.ArrayReader({
16753 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16757 * This would consume an Array like this:
16759 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16763 * Create a new JsonReader
16764 * @param {Object} meta Metadata configuration options.
16765 * @param {Object|Array} recordType Either an Array of field definition objects
16767 * @cfg {Array} fields Array of field definition objects
16768 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16769 * as specified to {@link Roo.data.Record#create},
16770 * or an {@link Roo.data.Record} object
16773 * created using {@link Roo.data.Record#create}.
16775 Roo.data.ArrayReader = function(meta, recordType)
16777 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16780 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16783 * Create a data block containing Roo.data.Records from an XML document.
16784 * @param {Object} o An Array of row objects which represents the dataset.
16785 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16786 * a cache of Roo.data.Records.
16788 readRecords : function(o)
16790 var sid = this.meta ? this.meta.id : null;
16791 var recordType = this.recordType, fields = recordType.prototype.fields;
16794 for(var i = 0; i < root.length; i++){
16797 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16798 for(var j = 0, jlen = fields.length; j < jlen; j++){
16799 var f = fields.items[j];
16800 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16801 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16803 values[f.name] = v;
16805 var record = new recordType(values, id);
16807 records[records.length] = record;
16811 totalRecords : records.length
16814 // used when loading children.. @see loadDataFromChildren
16815 toLoadData: function(rec)
16817 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16818 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16829 * @class Roo.bootstrap.form.ComboBox
16830 * @extends Roo.bootstrap.form.TriggerField
16831 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16832 * @cfg {Boolean} append (true|false) default false
16833 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16834 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16835 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16836 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16837 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16838 * @cfg {Boolean} animate default true
16839 * @cfg {Boolean} emptyResultText only for touch device
16840 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16841 * @cfg {String} emptyTitle default ''
16842 * @cfg {Number} width fixed with? experimental
16844 * Create a new ComboBox.
16845 * @param {Object} config Configuration options
16847 Roo.bootstrap.form.ComboBox = function(config){
16848 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16852 * Fires when the dropdown list is expanded
16853 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16858 * Fires when the dropdown list is collapsed
16859 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863 * @event beforeselect
16864 * Fires before a list item is selected. Return false to cancel the selection.
16865 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16866 * @param {Roo.data.Record} record The data record returned from the underlying store
16867 * @param {Number} index The index of the selected item in the dropdown list
16869 'beforeselect' : true,
16872 * Fires when a list item is selected
16873 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16874 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16875 * @param {Number} index The index of the selected item in the dropdown list
16879 * @event beforequery
16880 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16881 * The event object passed has these properties:
16882 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16883 * @param {String} query The query
16884 * @param {Boolean} forceAll true to force "all" query
16885 * @param {Boolean} cancel true to cancel the query
16886 * @param {Object} e The query event object
16888 'beforequery': true,
16891 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16892 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16897 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16898 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16899 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16904 * Fires when the remove value from the combobox array
16905 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909 * @event afterremove
16910 * Fires when the remove value from the combobox array
16911 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16913 'afterremove' : true,
16915 * @event specialfilter
16916 * Fires when specialfilter
16917 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16919 'specialfilter' : true,
16922 * Fires when tick the element
16923 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927 * @event touchviewdisplay
16928 * Fires when touch view require special display (default is using displayField)
16929 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16930 * @param {Object} cfg set html .
16932 'touchviewdisplay' : true
16937 this.tickItems = [];
16939 this.selectedIndex = -1;
16940 if(this.mode == 'local'){
16941 if(config.queryDelay === undefined){
16942 this.queryDelay = 10;
16944 if(config.minChars === undefined){
16950 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16953 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16954 * rendering into an Roo.Editor, defaults to false)
16957 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16958 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16961 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16964 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16965 * the dropdown list (defaults to undefined, with no header element)
16969 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16973 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16975 listWidth: undefined,
16977 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16978 * mode = 'remote' or 'text' if mode = 'local')
16980 displayField: undefined,
16983 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16984 * mode = 'remote' or 'value' if mode = 'local').
16985 * Note: use of a valueField requires the user make a selection
16986 * in order for a value to be mapped.
16988 valueField: undefined,
16990 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16995 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16996 * field's data value (defaults to the underlying DOM element's name)
16998 hiddenName: undefined,
17000 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17004 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17006 selectedClass: 'active',
17009 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17013 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17014 * anchor positions (defaults to 'tl-bl')
17016 listAlign: 'tl-bl?',
17018 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17022 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17023 * query specified by the allQuery config option (defaults to 'query')
17025 triggerAction: 'query',
17027 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17028 * (defaults to 4, does not apply if editable = false)
17032 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17033 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17037 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17038 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17042 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17043 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17047 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17048 * when editable = true (defaults to false)
17050 selectOnFocus:false,
17052 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17054 queryParam: 'query',
17056 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17057 * when mode = 'remote' (defaults to 'Loading...')
17059 loadingText: 'Loading...',
17061 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17065 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17069 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17070 * traditional select (defaults to true)
17074 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17078 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17082 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17083 * listWidth has a higher value)
17087 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17088 * allow the user to set arbitrary text into the field (defaults to false)
17090 forceSelection:false,
17092 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17093 * if typeAhead = true (defaults to 250)
17095 typeAheadDelay : 250,
17097 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17098 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17100 valueNotFoundText : undefined,
17102 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17104 blockFocus : false,
17107 * @cfg {Boolean} disableClear Disable showing of clear button.
17109 disableClear : false,
17111 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17113 alwaysQuery : false,
17116 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17121 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17123 invalidClass : "has-warning",
17126 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17128 validClass : "has-success",
17131 * @cfg {Boolean} specialFilter (true|false) special filter default false
17133 specialFilter : false,
17136 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17138 mobileTouchView : true,
17141 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17143 useNativeIOS : false,
17146 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17148 mobile_restrict_height : false,
17150 ios_options : false,
17162 btnPosition : 'right',
17163 triggerList : true,
17164 showToggleBtn : true,
17166 emptyResultText: 'Empty',
17167 triggerText : 'Select',
17171 // element that contains real text value.. (when hidden is used..)
17173 getAutoCreate : function()
17178 * Render classic select for iso
17181 if(Roo.isIOS && this.useNativeIOS){
17182 cfg = this.getAutoCreateNativeIOS();
17190 if(Roo.isTouch && this.mobileTouchView){
17191 cfg = this.getAutoCreateTouchView();
17198 if(!this.tickable){
17199 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17204 * ComboBox with tickable selections
17207 var align = this.labelAlign || this.parentLabelAlign();
17210 cls : 'form-group roo-combobox-tickable' //input-group
17213 var btn_text_select = '';
17214 var btn_text_done = '';
17215 var btn_text_cancel = '';
17217 if (this.btn_text_show) {
17218 btn_text_select = 'Select';
17219 btn_text_done = 'Done';
17220 btn_text_cancel = 'Cancel';
17225 cls : 'tickable-buttons',
17230 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17231 //html : this.triggerText
17232 html: btn_text_select
17238 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17240 html: btn_text_done
17246 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17248 html: btn_text_cancel
17254 buttons.cn.unshift({
17256 cls: 'roo-select2-search-field-input'
17262 Roo.each(buttons.cn, function(c){
17264 c.cls += ' btn-' + _this.size;
17267 if (_this.disabled) {
17274 style : 'display: contents',
17279 cls: 'form-hidden-field'
17283 cls: 'roo-select2-choices',
17287 cls: 'roo-select2-search-field',
17298 cls: 'roo-select2-container input-group roo-select2-container-multi',
17304 // cls: 'typeahead typeahead-long dropdown-menu',
17305 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17310 if(this.hasFeedback && !this.allowBlank){
17314 cls: 'glyphicon form-control-feedback'
17317 combobox.cn.push(feedback);
17324 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17325 tooltip : 'This field is required'
17328 if (this.allowBlank) {
17331 style : 'display:none'
17334 if (align ==='left' && this.fieldLabel.length) {
17336 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17343 cls : 'control-label col-form-label',
17344 html : this.fieldLabel
17356 var labelCfg = cfg.cn[1];
17357 var contentCfg = cfg.cn[2];
17360 if(this.indicatorpos == 'right'){
17366 cls : 'control-label col-form-label',
17370 html : this.fieldLabel
17386 labelCfg = cfg.cn[0];
17387 contentCfg = cfg.cn[1];
17391 if(this.labelWidth > 12){
17392 labelCfg.style = "width: " + this.labelWidth + 'px';
17394 if(this.width * 1 > 0){
17395 contentCfg.style = "width: " + this.width + 'px';
17397 if(this.labelWidth < 13 && this.labelmd == 0){
17398 this.labelmd = this.labelWidth;
17401 if(this.labellg > 0){
17402 labelCfg.cls += ' col-lg-' + this.labellg;
17403 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17406 if(this.labelmd > 0){
17407 labelCfg.cls += ' col-md-' + this.labelmd;
17408 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17411 if(this.labelsm > 0){
17412 labelCfg.cls += ' col-sm-' + this.labelsm;
17413 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17416 if(this.labelxs > 0){
17417 labelCfg.cls += ' col-xs-' + this.labelxs;
17418 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17422 } else if ( this.fieldLabel.length) {
17423 // Roo.log(" label");
17428 //cls : 'input-group-addon',
17429 html : this.fieldLabel
17434 if(this.indicatorpos == 'right'){
17438 //cls : 'input-group-addon',
17439 html : this.fieldLabel
17449 // Roo.log(" no label && no align");
17456 ['xs','sm','md','lg'].map(function(size){
17457 if (settings[size]) {
17458 cfg.cls += ' col-' + size + '-' + settings[size];
17466 _initEventsCalled : false,
17469 initEvents: function()
17471 if (this._initEventsCalled) { // as we call render... prevent looping...
17474 this._initEventsCalled = true;
17477 throw "can not find store for combo";
17480 this.indicator = this.indicatorEl();
17482 this.store = Roo.factory(this.store, Roo.data);
17483 this.store.parent = this;
17485 // if we are building from html. then this element is so complex, that we can not really
17486 // use the rendered HTML.
17487 // so we have to trash and replace the previous code.
17488 if (Roo.XComponent.build_from_html) {
17489 // remove this element....
17490 var e = this.el.dom, k=0;
17491 while (e ) { e = e.previousSibling; ++k;}
17496 this.rendered = false;
17498 this.render(this.parent().getChildContainer(true), k);
17501 if(Roo.isIOS && this.useNativeIOS){
17502 this.initIOSView();
17510 if(Roo.isTouch && this.mobileTouchView){
17511 this.initTouchView();
17516 this.initTickableEvents();
17520 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17522 if(this.hiddenName){
17524 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17526 this.hiddenField.dom.value =
17527 this.hiddenValue !== undefined ? this.hiddenValue :
17528 this.value !== undefined ? this.value : '';
17530 // prevent input submission
17531 this.el.dom.removeAttribute('name');
17532 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17537 // this.el.dom.setAttribute('autocomplete', 'off');
17540 var cls = 'x-combo-list';
17542 //this.list = new Roo.Layer({
17543 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17549 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17550 _this.list.setWidth(lw);
17553 this.list.on('mouseover', this.onViewOver, this);
17554 this.list.on('mousemove', this.onViewMove, this);
17555 this.list.on('scroll', this.onViewScroll, this);
17558 this.list.swallowEvent('mousewheel');
17559 this.assetHeight = 0;
17562 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17563 this.assetHeight += this.header.getHeight();
17566 this.innerList = this.list.createChild({cls:cls+'-inner'});
17567 this.innerList.on('mouseover', this.onViewOver, this);
17568 this.innerList.on('mousemove', this.onViewMove, this);
17569 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17571 if(this.allowBlank && !this.pageSize && !this.disableClear){
17572 this.footer = this.list.createChild({cls:cls+'-ft'});
17573 this.pageTb = new Roo.Toolbar(this.footer);
17577 this.footer = this.list.createChild({cls:cls+'-ft'});
17578 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17579 {pageSize: this.pageSize});
17583 if (this.pageTb && this.allowBlank && !this.disableClear) {
17585 this.pageTb.add(new Roo.Toolbar.Fill(), {
17586 cls: 'x-btn-icon x-btn-clear',
17588 handler: function()
17591 _this.clearValue();
17592 _this.onSelect(false, -1);
17597 this.assetHeight += this.footer.getHeight();
17602 this.tpl = Roo.bootstrap.version == 4 ?
17603 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17604 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17607 this.view = new Roo.View(this.list, this.tpl, {
17608 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17610 //this.view.wrapEl.setDisplayed(false);
17611 this.view.on('click', this.onViewClick, this);
17614 this.store.on('beforeload', this.onBeforeLoad, this);
17615 this.store.on('load', this.onLoad, this);
17616 this.store.on('loadexception', this.onLoadException, this);
17618 if(this.resizable){
17619 this.resizer = new Roo.Resizable(this.list, {
17620 pinned:true, handles:'se'
17622 this.resizer.on('resize', function(r, w, h){
17623 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17624 this.listWidth = w;
17625 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17626 this.restrictHeight();
17628 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17631 if(!this.editable){
17632 this.editable = true;
17633 this.setEditable(false);
17638 if (typeof(this.events.add.listeners) != 'undefined') {
17640 this.addicon = this.wrap.createChild(
17641 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17643 this.addicon.on('click', function(e) {
17644 this.fireEvent('add', this);
17647 if (typeof(this.events.edit.listeners) != 'undefined') {
17649 this.editicon = this.wrap.createChild(
17650 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17651 if (this.addicon) {
17652 this.editicon.setStyle('margin-left', '40px');
17654 this.editicon.on('click', function(e) {
17656 // we fire even if inothing is selected..
17657 this.fireEvent('edit', this, this.lastData );
17663 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17664 "up" : function(e){
17665 this.inKeyMode = true;
17669 "down" : function(e){
17670 if(!this.isExpanded()){
17671 this.onTriggerClick();
17673 this.inKeyMode = true;
17678 "enter" : function(e){
17679 // this.onViewClick();
17683 if(this.fireEvent("specialkey", this, e)){
17684 this.onViewClick(false);
17690 "esc" : function(e){
17694 "tab" : function(e){
17697 if(this.fireEvent("specialkey", this, e)){
17698 this.onViewClick(false);
17706 doRelay : function(foo, bar, hname){
17707 if(hname == 'down' || this.scope.isExpanded()){
17708 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17717 this.queryDelay = Math.max(this.queryDelay || 10,
17718 this.mode == 'local' ? 10 : 250);
17721 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17723 if(this.typeAhead){
17724 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17726 if(this.editable !== false){
17727 this.inputEl().on("keyup", this.onKeyUp, this);
17729 if(this.forceSelection){
17730 this.inputEl().on('blur', this.doForce, this);
17734 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17735 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17739 initTickableEvents: function()
17743 if(this.hiddenName){
17745 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17747 this.hiddenField.dom.value =
17748 this.hiddenValue !== undefined ? this.hiddenValue :
17749 this.value !== undefined ? this.value : '';
17751 // prevent input submission
17752 this.el.dom.removeAttribute('name');
17753 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17758 // this.list = this.el.select('ul.dropdown-menu',true).first();
17760 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17761 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17762 if(this.triggerList){
17763 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17766 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17767 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17769 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17770 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17772 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17773 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17775 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17776 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17777 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17780 this.cancelBtn.hide();
17785 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17786 _this.list.setWidth(lw);
17789 this.list.on('mouseover', this.onViewOver, this);
17790 this.list.on('mousemove', this.onViewMove, this);
17792 this.list.on('scroll', this.onViewScroll, this);
17795 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17796 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17799 this.view = new Roo.View(this.list, this.tpl, {
17804 selectedClass: this.selectedClass
17807 //this.view.wrapEl.setDisplayed(false);
17808 this.view.on('click', this.onViewClick, this);
17812 this.store.on('beforeload', this.onBeforeLoad, this);
17813 this.store.on('load', this.onLoad, this);
17814 this.store.on('loadexception', this.onLoadException, this);
17817 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17818 "up" : function(e){
17819 this.inKeyMode = true;
17823 "down" : function(e){
17824 this.inKeyMode = true;
17828 "enter" : function(e){
17829 if(this.fireEvent("specialkey", this, e)){
17830 this.onViewClick(false);
17836 "esc" : function(e){
17837 this.onTickableFooterButtonClick(e, false, false);
17840 "tab" : function(e){
17841 this.fireEvent("specialkey", this, e);
17843 this.onTickableFooterButtonClick(e, false, false);
17850 doRelay : function(e, fn, key){
17851 if(this.scope.isExpanded()){
17852 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17861 this.queryDelay = Math.max(this.queryDelay || 10,
17862 this.mode == 'local' ? 10 : 250);
17865 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17867 if(this.typeAhead){
17868 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17871 if(this.editable !== false){
17872 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17875 this.indicator = this.indicatorEl();
17877 if(this.indicator){
17878 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17879 this.indicator.hide();
17884 onDestroy : function(){
17886 this.view.setStore(null);
17887 this.view.el.removeAllListeners();
17888 this.view.el.remove();
17889 this.view.purgeListeners();
17892 this.list.dom.innerHTML = '';
17896 this.store.un('beforeload', this.onBeforeLoad, this);
17897 this.store.un('load', this.onLoad, this);
17898 this.store.un('loadexception', this.onLoadException, this);
17900 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17904 fireKey : function(e){
17905 if(e.isNavKeyPress() && !this.list.isVisible()){
17906 this.fireEvent("specialkey", this, e);
17911 onResize: function(w, h)
17915 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17917 // if(typeof w != 'number'){
17918 // // we do not handle it!?!?
17921 // var tw = this.trigger.getWidth();
17922 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17923 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17925 // this.inputEl().setWidth( this.adjustWidth('input', x));
17927 // //this.trigger.setStyle('left', x+'px');
17929 // if(this.list && this.listWidth === undefined){
17930 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17931 // this.list.setWidth(lw);
17932 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17940 * Allow or prevent the user from directly editing the field text. If false is passed,
17941 * the user will only be able to select from the items defined in the dropdown list. This method
17942 * is the runtime equivalent of setting the 'editable' config option at config time.
17943 * @param {Boolean} value True to allow the user to directly edit the field text
17945 setEditable : function(value){
17946 if(value == this.editable){
17949 this.editable = value;
17951 this.inputEl().dom.setAttribute('readOnly', true);
17952 this.inputEl().on('mousedown', this.onTriggerClick, this);
17953 this.inputEl().addClass('x-combo-noedit');
17955 this.inputEl().dom.removeAttribute('readOnly');
17956 this.inputEl().un('mousedown', this.onTriggerClick, this);
17957 this.inputEl().removeClass('x-combo-noedit');
17963 onBeforeLoad : function(combo,opts){
17964 if(!this.hasFocus){
17968 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17970 this.restrictHeight();
17971 this.selectedIndex = -1;
17975 onLoad : function(){
17977 this.hasQuery = false;
17979 if(!this.hasFocus){
17983 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17984 this.loading.hide();
17987 if(this.store.getCount() > 0){
17990 this.restrictHeight();
17991 if(this.lastQuery == this.allQuery){
17992 if(this.editable && !this.tickable){
17993 this.inputEl().dom.select();
17997 !this.selectByValue(this.value, true) &&
18000 !this.store.lastOptions ||
18001 typeof(this.store.lastOptions.add) == 'undefined' ||
18002 this.store.lastOptions.add != true
18005 this.select(0, true);
18008 if(this.autoFocus){
18011 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18012 this.taTask.delay(this.typeAheadDelay);
18016 this.onEmptyResults();
18022 onLoadException : function()
18024 this.hasQuery = false;
18026 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18027 this.loading.hide();
18030 if(this.tickable && this.editable){
18035 // only causes errors at present
18036 //Roo.log(this.store.reader.jsonData);
18037 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18039 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18045 onTypeAhead : function(){
18046 if(this.store.getCount() > 0){
18047 var r = this.store.getAt(0);
18048 var newValue = r.data[this.displayField];
18049 var len = newValue.length;
18050 var selStart = this.getRawValue().length;
18052 if(selStart != len){
18053 this.setRawValue(newValue);
18054 this.selectText(selStart, newValue.length);
18060 onSelect : function(record, index){
18062 if(this.fireEvent('beforeselect', this, record, index) !== false){
18064 this.setFromData(index > -1 ? record.data : false);
18067 this.fireEvent('select', this, record, index);
18072 * Returns the currently selected field value or empty string if no value is set.
18073 * @return {String} value The selected value
18075 getValue : function()
18077 if(Roo.isIOS && this.useNativeIOS){
18078 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18082 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18085 if(this.valueField){
18086 return typeof this.value != 'undefined' ? this.value : '';
18088 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18092 getRawValue : function()
18094 if(Roo.isIOS && this.useNativeIOS){
18095 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18098 var v = this.inputEl().getValue();
18104 * Clears any text/value currently set in the field
18106 clearValue : function(){
18108 if(this.hiddenField){
18109 this.hiddenField.dom.value = '';
18112 this.setRawValue('');
18113 this.lastSelectionText = '';
18114 this.lastData = false;
18116 var close = this.closeTriggerEl();
18127 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18128 * will be displayed in the field. If the value does not match the data value of an existing item,
18129 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18130 * Otherwise the field will be blank (although the value will still be set).
18131 * @param {String} value The value to match
18133 setValue : function(v)
18135 if(Roo.isIOS && this.useNativeIOS){
18136 this.setIOSValue(v);
18146 if(this.valueField){
18147 var r = this.findRecord(this.valueField, v);
18149 text = r.data[this.displayField];
18150 }else if(this.valueNotFoundText !== undefined){
18151 text = this.valueNotFoundText;
18154 this.lastSelectionText = text;
18155 if(this.hiddenField){
18156 this.hiddenField.dom.value = v;
18158 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18161 var close = this.closeTriggerEl();
18164 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18170 * @property {Object} the last set data for the element
18175 * Sets the value of the field based on a object which is related to the record format for the store.
18176 * @param {Object} value the value to set as. or false on reset?
18178 setFromData : function(o){
18185 var dv = ''; // display value
18186 var vv = ''; // value value..
18188 if (this.displayField) {
18189 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18191 // this is an error condition!!!
18192 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18195 if(this.valueField){
18196 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18199 var close = this.closeTriggerEl();
18202 if(dv.length || vv * 1 > 0){
18204 this.blockFocus=true;
18210 if(this.hiddenField){
18211 this.hiddenField.dom.value = vv;
18213 this.lastSelectionText = dv;
18214 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18218 // no hidden field.. - we store the value in 'value', but still display
18219 // display field!!!!
18220 this.lastSelectionText = dv;
18221 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18228 reset : function(){
18229 // overridden so that last data is reset..
18236 this.setValue(this.originalValue);
18237 //this.clearInvalid();
18238 this.lastData = false;
18240 this.view.clearSelections();
18246 findRecord : function(prop, value){
18248 if(this.store.getCount() > 0){
18249 this.store.each(function(r){
18250 if(r.data[prop] == value){
18260 getName: function()
18262 // returns hidden if it's set..
18263 if (!this.rendered) {return ''};
18264 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18268 onViewMove : function(e, t){
18269 this.inKeyMode = false;
18273 onViewOver : function(e, t){
18274 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18277 var item = this.view.findItemFromChild(t);
18280 var index = this.view.indexOf(item);
18281 this.select(index, false);
18286 onViewClick : function(view, doFocus, el, e)
18288 var index = this.view.getSelectedIndexes()[0];
18290 var r = this.store.getAt(index);
18294 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18301 Roo.each(this.tickItems, function(v,k){
18303 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18305 _this.tickItems.splice(k, 1);
18307 if(typeof(e) == 'undefined' && view == false){
18308 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18320 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18321 this.tickItems.push(r.data);
18324 if(typeof(e) == 'undefined' && view == false){
18325 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18332 this.onSelect(r, index);
18334 if(doFocus !== false && !this.blockFocus){
18335 this.inputEl().focus();
18340 restrictHeight : function(){
18341 //this.innerList.dom.style.height = '';
18342 //var inner = this.innerList.dom;
18343 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18344 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18345 //this.list.beginUpdate();
18346 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18347 this.list.alignTo(this.inputEl(), this.listAlign);
18348 this.list.alignTo(this.inputEl(), this.listAlign);
18349 //this.list.endUpdate();
18353 onEmptyResults : function(){
18355 if(this.tickable && this.editable){
18356 this.hasFocus = false;
18357 this.restrictHeight();
18365 * Returns true if the dropdown list is expanded, else false.
18367 isExpanded : function(){
18368 return this.list.isVisible();
18372 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18373 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18374 * @param {String} value The data value of the item to select
18375 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18376 * selected item if it is not currently in view (defaults to true)
18377 * @return {Boolean} True if the value matched an item in the list, else false
18379 selectByValue : function(v, scrollIntoView){
18380 if(v !== undefined && v !== null){
18381 var r = this.findRecord(this.valueField || this.displayField, v);
18383 this.select(this.store.indexOf(r), scrollIntoView);
18391 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18392 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18393 * @param {Number} index The zero-based index of the list item to select
18394 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18395 * selected item if it is not currently in view (defaults to true)
18397 select : function(index, scrollIntoView){
18398 this.selectedIndex = index;
18399 this.view.select(index);
18400 if(scrollIntoView !== false){
18401 var el = this.view.getNode(index);
18403 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18406 this.list.scrollChildIntoView(el, false);
18412 selectNext : function(){
18413 var ct = this.store.getCount();
18415 if(this.selectedIndex == -1){
18417 }else if(this.selectedIndex < ct-1){
18418 this.select(this.selectedIndex+1);
18424 selectPrev : function(){
18425 var ct = this.store.getCount();
18427 if(this.selectedIndex == -1){
18429 }else if(this.selectedIndex != 0){
18430 this.select(this.selectedIndex-1);
18436 onKeyUp : function(e){
18437 if(this.editable !== false && !e.isSpecialKey()){
18438 this.lastKey = e.getKey();
18439 this.dqTask.delay(this.queryDelay);
18444 validateBlur : function(){
18445 return !this.list || !this.list.isVisible();
18449 initQuery : function(){
18451 var v = this.getRawValue();
18453 if(this.tickable && this.editable){
18454 v = this.tickableInputEl().getValue();
18461 doForce : function(){
18462 if(this.inputEl().dom.value.length > 0){
18463 this.inputEl().dom.value =
18464 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18470 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18471 * query allowing the query action to be canceled if needed.
18472 * @param {String} query The SQL query to execute
18473 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18474 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18475 * saved in the current store (defaults to false)
18477 doQuery : function(q, forceAll){
18479 if(q === undefined || q === null){
18484 forceAll: forceAll,
18488 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18493 forceAll = qe.forceAll;
18494 if(forceAll === true || (q.length >= this.minChars)){
18496 this.hasQuery = true;
18498 if(this.lastQuery != q || this.alwaysQuery){
18499 this.lastQuery = q;
18500 if(this.mode == 'local'){
18501 this.selectedIndex = -1;
18503 this.store.clearFilter();
18506 if(this.specialFilter){
18507 this.fireEvent('specialfilter', this);
18512 this.store.filter(this.displayField, q);
18515 this.store.fireEvent("datachanged", this.store);
18522 this.store.baseParams[this.queryParam] = q;
18524 var options = {params : this.getParams(q)};
18527 options.add = true;
18528 options.params.start = this.page * this.pageSize;
18531 this.store.load(options);
18534 * this code will make the page width larger, at the beginning, the list not align correctly,
18535 * we should expand the list on onLoad
18536 * so command out it
18541 this.selectedIndex = -1;
18546 this.loadNext = false;
18550 getParams : function(q){
18552 //p[this.queryParam] = q;
18556 p.limit = this.pageSize;
18562 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18564 collapse : function(){
18565 if(!this.isExpanded()){
18571 this.hasFocus = false;
18575 this.cancelBtn.hide();
18576 this.trigger.show();
18579 this.tickableInputEl().dom.value = '';
18580 this.tickableInputEl().blur();
18585 Roo.get(document).un('mousedown', this.collapseIf, this);
18586 Roo.get(document).un('mousewheel', this.collapseIf, this);
18587 if (!this.editable) {
18588 Roo.get(document).un('keydown', this.listKeyPress, this);
18590 this.fireEvent('collapse', this);
18596 collapseIf : function(e){
18597 var in_combo = e.within(this.el);
18598 var in_list = e.within(this.list);
18599 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18601 if (in_combo || in_list || is_list) {
18602 //e.stopPropagation();
18607 this.onTickableFooterButtonClick(e, false, false);
18615 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18617 expand : function(){
18619 if(this.isExpanded() || !this.hasFocus){
18623 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18624 this.list.setWidth(lw);
18630 this.restrictHeight();
18634 this.tickItems = Roo.apply([], this.item);
18637 this.cancelBtn.show();
18638 this.trigger.hide();
18641 this.tickableInputEl().focus();
18646 Roo.get(document).on('mousedown', this.collapseIf, this);
18647 Roo.get(document).on('mousewheel', this.collapseIf, this);
18648 if (!this.editable) {
18649 Roo.get(document).on('keydown', this.listKeyPress, this);
18652 this.fireEvent('expand', this);
18656 // Implements the default empty TriggerField.onTriggerClick function
18657 onTriggerClick : function(e)
18659 Roo.log('trigger click');
18661 if(this.disabled || !this.triggerList){
18666 this.loadNext = false;
18668 if(this.isExpanded()){
18670 if (!this.blockFocus) {
18671 this.inputEl().focus();
18675 this.hasFocus = true;
18676 if(this.triggerAction == 'all') {
18677 this.doQuery(this.allQuery, true);
18679 this.doQuery(this.getRawValue());
18681 if (!this.blockFocus) {
18682 this.inputEl().focus();
18687 onTickableTriggerClick : function(e)
18694 this.loadNext = false;
18695 this.hasFocus = true;
18697 if(this.triggerAction == 'all') {
18698 this.doQuery(this.allQuery, true);
18700 this.doQuery(this.getRawValue());
18704 onSearchFieldClick : function(e)
18706 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18707 this.onTickableFooterButtonClick(e, false, false);
18711 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18716 this.loadNext = false;
18717 this.hasFocus = true;
18719 if(this.triggerAction == 'all') {
18720 this.doQuery(this.allQuery, true);
18722 this.doQuery(this.getRawValue());
18726 listKeyPress : function(e)
18728 //Roo.log('listkeypress');
18729 // scroll to first matching element based on key pres..
18730 if (e.isSpecialKey()) {
18733 var k = String.fromCharCode(e.getKey()).toUpperCase();
18736 var csel = this.view.getSelectedNodes();
18737 var cselitem = false;
18739 var ix = this.view.indexOf(csel[0]);
18740 cselitem = this.store.getAt(ix);
18741 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18747 this.store.each(function(v) {
18749 // start at existing selection.
18750 if (cselitem.id == v.id) {
18756 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18757 match = this.store.indexOf(v);
18763 if (match === false) {
18764 return true; // no more action?
18767 this.view.select(match);
18768 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18769 sn.scrollIntoView(sn.dom.parentNode, false);
18772 onViewScroll : function(e, t){
18774 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){
18778 this.hasQuery = true;
18780 this.loading = this.list.select('.loading', true).first();
18782 if(this.loading === null){
18783 this.list.createChild({
18785 cls: 'loading roo-select2-more-results roo-select2-active',
18786 html: 'Loading more results...'
18789 this.loading = this.list.select('.loading', true).first();
18791 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18793 this.loading.hide();
18796 this.loading.show();
18801 this.loadNext = true;
18803 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18808 addItem : function(o)
18810 var dv = ''; // display value
18812 if (this.displayField) {
18813 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18815 // this is an error condition!!!
18816 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18823 var choice = this.choices.createChild({
18825 cls: 'roo-select2-search-choice',
18834 cls: 'roo-select2-search-choice-close fa fa-times',
18839 }, this.searchField);
18841 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18843 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18851 this.inputEl().dom.value = '';
18856 onRemoveItem : function(e, _self, o)
18858 e.preventDefault();
18860 this.lastItem = Roo.apply([], this.item);
18862 var index = this.item.indexOf(o.data) * 1;
18865 Roo.log('not this item?!');
18869 this.item.splice(index, 1);
18874 this.fireEvent('remove', this, e);
18880 syncValue : function()
18882 if(!this.item.length){
18889 Roo.each(this.item, function(i){
18890 if(_this.valueField){
18891 value.push(i[_this.valueField]);
18898 this.value = value.join(',');
18900 if(this.hiddenField){
18901 this.hiddenField.dom.value = this.value;
18904 this.store.fireEvent("datachanged", this.store);
18909 clearItem : function()
18911 if(!this.multiple){
18917 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18925 if(this.tickable && !Roo.isTouch){
18926 this.view.refresh();
18930 inputEl: function ()
18932 if(Roo.isIOS && this.useNativeIOS){
18933 return this.el.select('select.roo-ios-select', true).first();
18936 if(Roo.isTouch && this.mobileTouchView){
18937 return this.el.select('input.form-control',true).first();
18941 return this.searchField;
18944 return this.el.select('input.form-control',true).first();
18947 onTickableFooterButtonClick : function(e, btn, el)
18949 e.preventDefault();
18951 this.lastItem = Roo.apply([], this.item);
18953 if(btn && btn.name == 'cancel'){
18954 this.tickItems = Roo.apply([], this.item);
18963 Roo.each(this.tickItems, function(o){
18971 validate : function()
18973 if(this.getVisibilityEl().hasClass('hidden')){
18977 var v = this.getRawValue();
18980 v = this.getValue();
18983 if(this.disabled || this.allowBlank || v.length){
18988 this.markInvalid();
18992 tickableInputEl : function()
18994 if(!this.tickable || !this.editable){
18995 return this.inputEl();
18998 return this.inputEl().select('.roo-select2-search-field-input', true).first();
19002 getAutoCreateTouchView : function()
19007 cls: 'form-group' //input-group
19013 type : this.inputType,
19014 cls : 'form-control x-combo-noedit',
19015 autocomplete: 'new-password',
19016 placeholder : this.placeholder || '',
19021 input.name = this.name;
19025 input.cls += ' input-' + this.size;
19028 if (this.disabled) {
19029 input.disabled = true;
19033 cls : 'roo-combobox-wrap',
19040 inputblock.cls += ' input-group';
19042 inputblock.cn.unshift({
19044 cls : 'input-group-addon input-group-prepend input-group-text',
19049 if(this.removable && !this.multiple){
19050 inputblock.cls += ' roo-removable';
19052 inputblock.cn.push({
19055 cls : 'roo-combo-removable-btn close'
19059 if(this.hasFeedback && !this.allowBlank){
19061 inputblock.cls += ' has-feedback';
19063 inputblock.cn.push({
19065 cls: 'glyphicon form-control-feedback'
19072 inputblock.cls += (this.before) ? '' : ' input-group';
19074 inputblock.cn.push({
19076 cls : 'input-group-addon input-group-append input-group-text',
19082 var ibwrap = inputblock;
19087 cls: 'roo-select2-choices',
19091 cls: 'roo-select2-search-field',
19104 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19109 cls: 'form-hidden-field'
19115 if(!this.multiple && this.showToggleBtn){
19121 if (this.caret != false) {
19124 cls: 'fa fa-' + this.caret
19131 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19133 Roo.bootstrap.version == 3 ? caret : '',
19136 cls: 'combobox-clear',
19150 combobox.cls += ' roo-select2-container-multi';
19153 var required = this.allowBlank ? {
19155 style: 'display: none'
19158 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19159 tooltip : 'This field is required'
19162 var align = this.labelAlign || this.parentLabelAlign();
19164 if (align ==='left' && this.fieldLabel.length) {
19170 cls : 'control-label col-form-label',
19171 html : this.fieldLabel
19175 cls : 'roo-combobox-wrap ',
19182 var labelCfg = cfg.cn[1];
19183 var contentCfg = cfg.cn[2];
19186 if(this.indicatorpos == 'right'){
19191 cls : 'control-label col-form-label',
19195 html : this.fieldLabel
19201 cls : "roo-combobox-wrap ",
19209 labelCfg = cfg.cn[0];
19210 contentCfg = cfg.cn[1];
19215 if(this.labelWidth > 12){
19216 labelCfg.style = "width: " + this.labelWidth + 'px';
19219 if(this.labelWidth < 13 && this.labelmd == 0){
19220 this.labelmd = this.labelWidth;
19223 if(this.labellg > 0){
19224 labelCfg.cls += ' col-lg-' + this.labellg;
19225 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19228 if(this.labelmd > 0){
19229 labelCfg.cls += ' col-md-' + this.labelmd;
19230 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19233 if(this.labelsm > 0){
19234 labelCfg.cls += ' col-sm-' + this.labelsm;
19235 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19238 if(this.labelxs > 0){
19239 labelCfg.cls += ' col-xs-' + this.labelxs;
19240 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19244 } else if ( this.fieldLabel.length) {
19249 cls : 'control-label',
19250 html : this.fieldLabel
19261 if(this.indicatorpos == 'right'){
19265 cls : 'control-label',
19266 html : this.fieldLabel,
19284 var settings = this;
19286 ['xs','sm','md','lg'].map(function(size){
19287 if (settings[size]) {
19288 cfg.cls += ' col-' + size + '-' + settings[size];
19295 initTouchView : function()
19297 this.renderTouchView();
19299 this.touchViewEl.on('scroll', function(){
19300 this.el.dom.scrollTop = 0;
19303 this.originalValue = this.getValue();
19305 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19307 this.inputEl().on("click", this.showTouchView, this);
19308 if (this.triggerEl) {
19309 this.triggerEl.on("click", this.showTouchView, this);
19313 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19314 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19316 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19318 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19319 this.store.on('load', this.onTouchViewLoad, this);
19320 this.store.on('loadexception', this.onTouchViewLoadException, this);
19322 if(this.hiddenName){
19324 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19326 this.hiddenField.dom.value =
19327 this.hiddenValue !== undefined ? this.hiddenValue :
19328 this.value !== undefined ? this.value : '';
19330 this.el.dom.removeAttribute('name');
19331 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19335 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19336 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19339 if(this.removable && !this.multiple){
19340 var close = this.closeTriggerEl();
19342 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19343 close.on('click', this.removeBtnClick, this, close);
19347 * fix the bug in Safari iOS8
19349 this.inputEl().on("focus", function(e){
19350 document.activeElement.blur();
19353 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19360 renderTouchView : function()
19362 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19363 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19365 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19366 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19368 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19369 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19370 this.touchViewBodyEl.setStyle('overflow', 'auto');
19372 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19373 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19375 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19376 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19380 showTouchView : function()
19386 this.touchViewHeaderEl.hide();
19388 if(this.modalTitle.length){
19389 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19390 this.touchViewHeaderEl.show();
19393 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19394 this.touchViewEl.show();
19396 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19398 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19399 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19401 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19403 if(this.modalTitle.length){
19404 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19407 this.touchViewBodyEl.setHeight(bodyHeight);
19411 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19413 this.touchViewEl.addClass(['in','show']);
19416 if(this._touchViewMask){
19417 Roo.get(document.body).addClass("x-body-masked");
19418 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19419 this._touchViewMask.setStyle('z-index', 10000);
19420 this._touchViewMask.addClass('show');
19423 this.doTouchViewQuery();
19427 hideTouchView : function()
19429 this.touchViewEl.removeClass(['in','show']);
19433 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19435 this.touchViewEl.setStyle('display', 'none');
19438 if(this._touchViewMask){
19439 this._touchViewMask.removeClass('show');
19440 Roo.get(document.body).removeClass("x-body-masked");
19444 setTouchViewValue : function()
19451 Roo.each(this.tickItems, function(o){
19456 this.hideTouchView();
19459 doTouchViewQuery : function()
19468 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19472 if(!this.alwaysQuery || this.mode == 'local'){
19473 this.onTouchViewLoad();
19480 onTouchViewBeforeLoad : function(combo,opts)
19486 onTouchViewLoad : function()
19488 if(this.store.getCount() < 1){
19489 this.onTouchViewEmptyResults();
19493 this.clearTouchView();
19495 var rawValue = this.getRawValue();
19497 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19499 this.tickItems = [];
19501 this.store.data.each(function(d, rowIndex){
19502 var row = this.touchViewListGroup.createChild(template);
19504 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19505 row.addClass(d.data.cls);
19508 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19511 html : d.data[this.displayField]
19514 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19515 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19518 row.removeClass('selected');
19519 if(!this.multiple && this.valueField &&
19520 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19523 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19524 row.addClass('selected');
19527 if(this.multiple && this.valueField &&
19528 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19532 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19533 this.tickItems.push(d.data);
19536 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19540 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19542 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19544 if(this.modalTitle.length){
19545 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19548 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19550 if(this.mobile_restrict_height && listHeight < bodyHeight){
19551 this.touchViewBodyEl.setHeight(listHeight);
19556 if(firstChecked && listHeight > bodyHeight){
19557 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19562 onTouchViewLoadException : function()
19564 this.hideTouchView();
19567 onTouchViewEmptyResults : function()
19569 this.clearTouchView();
19571 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19573 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19577 clearTouchView : function()
19579 this.touchViewListGroup.dom.innerHTML = '';
19582 onTouchViewClick : function(e, el, o)
19584 e.preventDefault();
19587 var rowIndex = o.rowIndex;
19589 var r = this.store.getAt(rowIndex);
19591 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19593 if(!this.multiple){
19594 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19595 c.dom.removeAttribute('checked');
19598 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19600 this.setFromData(r.data);
19602 var close = this.closeTriggerEl();
19608 this.hideTouchView();
19610 this.fireEvent('select', this, r, rowIndex);
19615 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19616 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19617 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19621 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19622 this.addItem(r.data);
19623 this.tickItems.push(r.data);
19627 getAutoCreateNativeIOS : function()
19630 cls: 'form-group' //input-group,
19635 cls : 'roo-ios-select'
19639 combobox.name = this.name;
19642 if (this.disabled) {
19643 combobox.disabled = true;
19646 var settings = this;
19648 ['xs','sm','md','lg'].map(function(size){
19649 if (settings[size]) {
19650 cfg.cls += ' col-' + size + '-' + settings[size];
19660 initIOSView : function()
19662 this.store.on('load', this.onIOSViewLoad, this);
19667 onIOSViewLoad : function()
19669 if(this.store.getCount() < 1){
19673 this.clearIOSView();
19675 if(this.allowBlank) {
19677 var default_text = '-- SELECT --';
19679 if(this.placeholder.length){
19680 default_text = this.placeholder;
19683 if(this.emptyTitle.length){
19684 default_text += ' - ' + this.emptyTitle + ' -';
19687 var opt = this.inputEl().createChild({
19690 html : default_text
19694 o[this.valueField] = 0;
19695 o[this.displayField] = default_text;
19697 this.ios_options.push({
19704 this.store.data.each(function(d, rowIndex){
19708 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19709 html = d.data[this.displayField];
19714 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19715 value = d.data[this.valueField];
19724 if(this.value == d.data[this.valueField]){
19725 option['selected'] = true;
19728 var opt = this.inputEl().createChild(option);
19730 this.ios_options.push({
19737 this.inputEl().on('change', function(){
19738 this.fireEvent('select', this);
19743 clearIOSView: function()
19745 this.inputEl().dom.innerHTML = '';
19747 this.ios_options = [];
19750 setIOSValue: function(v)
19754 if(!this.ios_options){
19758 Roo.each(this.ios_options, function(opts){
19760 opts.el.dom.removeAttribute('selected');
19762 if(opts.data[this.valueField] != v){
19766 opts.el.dom.setAttribute('selected', true);
19772 * @cfg {Boolean} grow
19776 * @cfg {Number} growMin
19780 * @cfg {Number} growMax
19789 Roo.apply(Roo.bootstrap.form.ComboBox, {
19793 cls: 'modal-header',
19815 cls: 'list-group-item',
19819 cls: 'roo-combobox-list-group-item-value'
19823 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19837 listItemCheckbox : {
19839 cls: 'list-group-item',
19843 cls: 'roo-combobox-list-group-item-value'
19847 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19863 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19868 cls: 'modal-footer',
19876 cls: 'col-xs-6 text-left',
19879 cls: 'btn btn-danger roo-touch-view-cancel',
19885 cls: 'col-xs-6 text-right',
19888 cls: 'btn btn-success roo-touch-view-ok',
19899 Roo.apply(Roo.bootstrap.form.ComboBox, {
19901 touchViewTemplate : {
19903 cls: 'modal fade roo-combobox-touch-view',
19907 cls: 'modal-dialog',
19908 style : 'position:fixed', // we have to fix position....
19912 cls: 'modal-content',
19914 Roo.bootstrap.form.ComboBox.header,
19915 Roo.bootstrap.form.ComboBox.body,
19916 Roo.bootstrap.form.ComboBox.footer
19925 * Ext JS Library 1.1.1
19926 * Copyright(c) 2006-2007, Ext JS, LLC.
19928 * Originally Released Under LGPL - original licence link has changed is not relivant.
19931 * <script type="text/javascript">
19936 * @extends Roo.util.Observable
19937 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19938 * This class also supports single and multi selection modes. <br>
19939 * Create a data model bound view:
19941 var store = new Roo.data.Store(...);
19943 var view = new Roo.View({
19945 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19947 singleSelect: true,
19948 selectedClass: "ydataview-selected",
19952 // listen for node click?
19953 view.on("click", function(vw, index, node, e){
19954 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19958 dataModel.load("foobar.xml");
19960 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19962 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19963 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19965 * Note: old style constructor is still suported (container, template, config)
19968 * Create a new View
19969 * @param {Object} config The config object
19972 Roo.View = function(config, depreciated_tpl, depreciated_config){
19974 this.parent = false;
19976 if (typeof(depreciated_tpl) == 'undefined') {
19977 // new way.. - universal constructor.
19978 Roo.apply(this, config);
19979 this.el = Roo.get(this.el);
19982 this.el = Roo.get(config);
19983 this.tpl = depreciated_tpl;
19984 Roo.apply(this, depreciated_config);
19986 this.wrapEl = this.el.wrap().wrap();
19987 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19990 if(typeof(this.tpl) == "string"){
19991 this.tpl = new Roo.Template(this.tpl);
19993 // support xtype ctors..
19994 this.tpl = new Roo.factory(this.tpl, Roo);
19998 this.tpl.compile();
20003 * @event beforeclick
20004 * Fires before a click is processed. Returns false to cancel the default action.
20005 * @param {Roo.View} this
20006 * @param {Number} index The index of the target node
20007 * @param {HTMLElement} node The target node
20008 * @param {Roo.EventObject} e The raw event object
20010 "beforeclick" : true,
20013 * Fires when a template node is clicked.
20014 * @param {Roo.View} this
20015 * @param {Number} index The index of the target node
20016 * @param {HTMLElement} node The target node
20017 * @param {Roo.EventObject} e The raw event object
20022 * Fires when a template node is double clicked.
20023 * @param {Roo.View} this
20024 * @param {Number} index The index of the target node
20025 * @param {HTMLElement} node The target node
20026 * @param {Roo.EventObject} e The raw event object
20030 * @event contextmenu
20031 * Fires when a template node is right clicked.
20032 * @param {Roo.View} this
20033 * @param {Number} index The index of the target node
20034 * @param {HTMLElement} node The target node
20035 * @param {Roo.EventObject} e The raw event object
20037 "contextmenu" : true,
20039 * @event selectionchange
20040 * Fires when the selected nodes change.
20041 * @param {Roo.View} this
20042 * @param {Array} selections Array of the selected nodes
20044 "selectionchange" : true,
20047 * @event beforeselect
20048 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20049 * @param {Roo.View} this
20050 * @param {HTMLElement} node The node to be selected
20051 * @param {Array} selections Array of currently selected nodes
20053 "beforeselect" : true,
20055 * @event preparedata
20056 * Fires on every row to render, to allow you to change the data.
20057 * @param {Roo.View} this
20058 * @param {Object} data to be rendered (change this)
20060 "preparedata" : true
20068 "click": this.onClick,
20069 "dblclick": this.onDblClick,
20070 "contextmenu": this.onContextMenu,
20074 this.selections = [];
20076 this.cmp = new Roo.CompositeElementLite([]);
20078 this.store = Roo.factory(this.store, Roo.data);
20079 this.setStore(this.store, true);
20082 if ( this.footer && this.footer.xtype) {
20084 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20086 this.footer.dataSource = this.store;
20087 this.footer.container = fctr;
20088 this.footer = Roo.factory(this.footer, Roo);
20089 fctr.insertFirst(this.el);
20091 // this is a bit insane - as the paging toolbar seems to detach the el..
20092 // dom.parentNode.parentNode.parentNode
20093 // they get detached?
20097 Roo.View.superclass.constructor.call(this);
20102 Roo.extend(Roo.View, Roo.util.Observable, {
20105 * @cfg {Roo.data.Store} store Data store to load data from.
20110 * @cfg {String|Roo.Element} el The container element.
20115 * @cfg {String|Roo.Template} tpl The template used by this View
20119 * @cfg {String} dataName the named area of the template to use as the data area
20120 * Works with domtemplates roo-name="name"
20124 * @cfg {String} selectedClass The css class to add to selected nodes
20126 selectedClass : "x-view-selected",
20128 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20133 * @cfg {String} text to display on mask (default Loading)
20137 * @cfg {Boolean} multiSelect Allow multiple selection
20139 multiSelect : false,
20141 * @cfg {Boolean} singleSelect Allow single selection
20143 singleSelect: false,
20146 * @cfg {Boolean} toggleSelect - selecting
20148 toggleSelect : false,
20151 * @cfg {Boolean} tickable - selecting
20156 * Returns the element this view is bound to.
20157 * @return {Roo.Element}
20159 getEl : function(){
20160 return this.wrapEl;
20166 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20168 refresh : function(){
20169 //Roo.log('refresh');
20172 // if we are using something like 'domtemplate', then
20173 // the what gets used is:
20174 // t.applySubtemplate(NAME, data, wrapping data..)
20175 // the outer template then get' applied with
20176 // the store 'extra data'
20177 // and the body get's added to the
20178 // roo-name="data" node?
20179 // <span class='roo-tpl-{name}'></span> ?????
20183 this.clearSelections();
20184 this.el.update("");
20186 var records = this.store.getRange();
20187 if(records.length < 1) {
20189 // is this valid?? = should it render a template??
20191 this.el.update(this.emptyText);
20195 if (this.dataName) {
20196 this.el.update(t.apply(this.store.meta)); //????
20197 el = this.el.child('.roo-tpl-' + this.dataName);
20200 for(var i = 0, len = records.length; i < len; i++){
20201 var data = this.prepareData(records[i].data, i, records[i]);
20202 this.fireEvent("preparedata", this, data, i, records[i]);
20204 var d = Roo.apply({}, data);
20207 Roo.apply(d, {'roo-id' : Roo.id()});
20211 Roo.each(this.parent.item, function(item){
20212 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20215 Roo.apply(d, {'roo-data-checked' : 'checked'});
20219 html[html.length] = Roo.util.Format.trim(
20221 t.applySubtemplate(this.dataName, d, this.store.meta) :
20228 el.update(html.join(""));
20229 this.nodes = el.dom.childNodes;
20230 this.updateIndexes(0);
20235 * Function to override to reformat the data that is sent to
20236 * the template for each node.
20237 * DEPRICATED - use the preparedata event handler.
20238 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20239 * a JSON object for an UpdateManager bound view).
20241 prepareData : function(data, index, record)
20243 this.fireEvent("preparedata", this, data, index, record);
20247 onUpdate : function(ds, record){
20248 // Roo.log('on update');
20249 this.clearSelections();
20250 var index = this.store.indexOf(record);
20251 var n = this.nodes[index];
20252 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20253 n.parentNode.removeChild(n);
20254 this.updateIndexes(index, index);
20260 onAdd : function(ds, records, index)
20262 //Roo.log(['on Add', ds, records, index] );
20263 this.clearSelections();
20264 if(this.nodes.length == 0){
20268 var n = this.nodes[index];
20269 for(var i = 0, len = records.length; i < len; i++){
20270 var d = this.prepareData(records[i].data, i, records[i]);
20272 this.tpl.insertBefore(n, d);
20275 this.tpl.append(this.el, d);
20278 this.updateIndexes(index);
20281 onRemove : function(ds, record, index){
20282 // Roo.log('onRemove');
20283 this.clearSelections();
20284 var el = this.dataName ?
20285 this.el.child('.roo-tpl-' + this.dataName) :
20288 el.dom.removeChild(this.nodes[index]);
20289 this.updateIndexes(index);
20293 * Refresh an individual node.
20294 * @param {Number} index
20296 refreshNode : function(index){
20297 this.onUpdate(this.store, this.store.getAt(index));
20300 updateIndexes : function(startIndex, endIndex){
20301 var ns = this.nodes;
20302 startIndex = startIndex || 0;
20303 endIndex = endIndex || ns.length - 1;
20304 for(var i = startIndex; i <= endIndex; i++){
20305 ns[i].nodeIndex = i;
20310 * Changes the data store this view uses and refresh the view.
20311 * @param {Store} store
20313 setStore : function(store, initial){
20314 if(!initial && this.store){
20315 this.store.un("datachanged", this.refresh);
20316 this.store.un("add", this.onAdd);
20317 this.store.un("remove", this.onRemove);
20318 this.store.un("update", this.onUpdate);
20319 this.store.un("clear", this.refresh);
20320 this.store.un("beforeload", this.onBeforeLoad);
20321 this.store.un("load", this.onLoad);
20322 this.store.un("loadexception", this.onLoad);
20326 store.on("datachanged", this.refresh, this);
20327 store.on("add", this.onAdd, this);
20328 store.on("remove", this.onRemove, this);
20329 store.on("update", this.onUpdate, this);
20330 store.on("clear", this.refresh, this);
20331 store.on("beforeload", this.onBeforeLoad, this);
20332 store.on("load", this.onLoad, this);
20333 store.on("loadexception", this.onLoad, this);
20341 * onbeforeLoad - masks the loading area.
20344 onBeforeLoad : function(store,opts)
20346 //Roo.log('onBeforeLoad');
20348 this.el.update("");
20350 this.el.mask(this.mask ? this.mask : "Loading" );
20352 onLoad : function ()
20359 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20360 * @param {HTMLElement} node
20361 * @return {HTMLElement} The template node
20363 findItemFromChild : function(node){
20364 var el = this.dataName ?
20365 this.el.child('.roo-tpl-' + this.dataName,true) :
20368 if(!node || node.parentNode == el){
20371 var p = node.parentNode;
20372 while(p && p != el){
20373 if(p.parentNode == el){
20382 onClick : function(e){
20383 var item = this.findItemFromChild(e.getTarget());
20385 var index = this.indexOf(item);
20386 if(this.onItemClick(item, index, e) !== false){
20387 this.fireEvent("click", this, index, item, e);
20390 this.clearSelections();
20395 onContextMenu : function(e){
20396 var item = this.findItemFromChild(e.getTarget());
20398 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20403 onDblClick : function(e){
20404 var item = this.findItemFromChild(e.getTarget());
20406 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20410 onItemClick : function(item, index, e)
20412 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20415 if (this.toggleSelect) {
20416 var m = this.isSelected(item) ? 'unselect' : 'select';
20419 _t[m](item, true, false);
20422 if(this.multiSelect || this.singleSelect){
20423 if(this.multiSelect && e.shiftKey && this.lastSelection){
20424 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20426 this.select(item, this.multiSelect && e.ctrlKey);
20427 this.lastSelection = item;
20430 if(!this.tickable){
20431 e.preventDefault();
20439 * Get the number of selected nodes.
20442 getSelectionCount : function(){
20443 return this.selections.length;
20447 * Get the currently selected nodes.
20448 * @return {Array} An array of HTMLElements
20450 getSelectedNodes : function(){
20451 return this.selections;
20455 * Get the indexes of the selected nodes.
20458 getSelectedIndexes : function(){
20459 var indexes = [], s = this.selections;
20460 for(var i = 0, len = s.length; i < len; i++){
20461 indexes.push(s[i].nodeIndex);
20467 * Clear all selections
20468 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20470 clearSelections : function(suppressEvent){
20471 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20472 this.cmp.elements = this.selections;
20473 this.cmp.removeClass(this.selectedClass);
20474 this.selections = [];
20475 if(!suppressEvent){
20476 this.fireEvent("selectionchange", this, this.selections);
20482 * Returns true if the passed node is selected
20483 * @param {HTMLElement/Number} node The node or node index
20484 * @return {Boolean}
20486 isSelected : function(node){
20487 var s = this.selections;
20491 node = this.getNode(node);
20492 return s.indexOf(node) !== -1;
20497 * @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
20498 * @param {Boolean} keepExisting (optional) true to keep existing selections
20499 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20501 select : function(nodeInfo, keepExisting, suppressEvent){
20502 if(nodeInfo instanceof Array){
20504 this.clearSelections(true);
20506 for(var i = 0, len = nodeInfo.length; i < len; i++){
20507 this.select(nodeInfo[i], true, true);
20511 var node = this.getNode(nodeInfo);
20512 if(!node || this.isSelected(node)){
20513 return; // already selected.
20516 this.clearSelections(true);
20519 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20520 Roo.fly(node).addClass(this.selectedClass);
20521 this.selections.push(node);
20522 if(!suppressEvent){
20523 this.fireEvent("selectionchange", this, this.selections);
20531 * @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
20532 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20533 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20535 unselect : function(nodeInfo, keepExisting, suppressEvent)
20537 if(nodeInfo instanceof Array){
20538 Roo.each(this.selections, function(s) {
20539 this.unselect(s, nodeInfo);
20543 var node = this.getNode(nodeInfo);
20544 if(!node || !this.isSelected(node)){
20545 //Roo.log("not selected");
20546 return; // not selected.
20550 Roo.each(this.selections, function(s) {
20552 Roo.fly(node).removeClass(this.selectedClass);
20559 this.selections= ns;
20560 this.fireEvent("selectionchange", this, this.selections);
20564 * Gets a template node.
20565 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20566 * @return {HTMLElement} The node or null if it wasn't found
20568 getNode : function(nodeInfo){
20569 if(typeof nodeInfo == "string"){
20570 return document.getElementById(nodeInfo);
20571 }else if(typeof nodeInfo == "number"){
20572 return this.nodes[nodeInfo];
20578 * Gets a range template nodes.
20579 * @param {Number} startIndex
20580 * @param {Number} endIndex
20581 * @return {Array} An array of nodes
20583 getNodes : function(start, end){
20584 var ns = this.nodes;
20585 start = start || 0;
20586 end = typeof end == "undefined" ? ns.length - 1 : end;
20589 for(var i = start; i <= end; i++){
20593 for(var i = start; i >= end; i--){
20601 * Finds the index of the passed node
20602 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20603 * @return {Number} The index of the node or -1
20605 indexOf : function(node){
20606 node = this.getNode(node);
20607 if(typeof node.nodeIndex == "number"){
20608 return node.nodeIndex;
20610 var ns = this.nodes;
20611 for(var i = 0, len = ns.length; i < len; i++){
20622 * based on jquery fullcalendar
20626 Roo.bootstrap = Roo.bootstrap || {};
20628 * @class Roo.bootstrap.Calendar
20629 * @extends Roo.bootstrap.Component
20630 * Bootstrap Calendar class
20631 * @cfg {Boolean} loadMask (true|false) default false
20632 * @cfg {Object} header generate the user specific header of the calendar, default false
20635 * Create a new Container
20636 * @param {Object} config The config object
20641 Roo.bootstrap.Calendar = function(config){
20642 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20646 * Fires when a date is selected
20647 * @param {DatePicker} this
20648 * @param {Date} date The selected date
20652 * @event monthchange
20653 * Fires when the displayed month changes
20654 * @param {DatePicker} this
20655 * @param {Date} date The selected month
20657 'monthchange': true,
20659 * @event evententer
20660 * Fires when mouse over an event
20661 * @param {Calendar} this
20662 * @param {event} Event
20664 'evententer': true,
20666 * @event eventleave
20667 * Fires when the mouse leaves an
20668 * @param {Calendar} this
20671 'eventleave': true,
20673 * @event eventclick
20674 * Fires when the mouse click an
20675 * @param {Calendar} this
20684 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20687 * @cfg {Roo.data.Store} store
20688 * The data source for the calendar
20692 * @cfg {Number} startDay
20693 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20701 getAutoCreate : function(){
20704 var fc_button = function(name, corner, style, content ) {
20705 return Roo.apply({},{
20707 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20709 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20712 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20723 style : 'width:100%',
20730 cls : 'fc-header-left',
20732 fc_button('prev', 'left', 'arrow', '‹' ),
20733 fc_button('next', 'right', 'arrow', '›' ),
20734 { tag: 'span', cls: 'fc-header-space' },
20735 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20743 cls : 'fc-header-center',
20747 cls: 'fc-header-title',
20750 html : 'month / year'
20758 cls : 'fc-header-right',
20760 /* fc_button('month', 'left', '', 'month' ),
20761 fc_button('week', '', '', 'week' ),
20762 fc_button('day', 'right', '', 'day' )
20774 header = this.header;
20777 var cal_heads = function() {
20779 // fixme - handle this.
20781 for (var i =0; i < Date.dayNames.length; i++) {
20782 var d = Date.dayNames[i];
20785 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20786 html : d.substring(0,3)
20790 ret[0].cls += ' fc-first';
20791 ret[6].cls += ' fc-last';
20794 var cal_cell = function(n) {
20797 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20802 cls: 'fc-day-number',
20806 cls: 'fc-day-content',
20810 style: 'position: relative;' // height: 17px;
20822 var cal_rows = function() {
20825 for (var r = 0; r < 6; r++) {
20832 for (var i =0; i < Date.dayNames.length; i++) {
20833 var d = Date.dayNames[i];
20834 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20837 row.cn[0].cls+=' fc-first';
20838 row.cn[0].cn[0].style = 'min-height:90px';
20839 row.cn[6].cls+=' fc-last';
20843 ret[0].cls += ' fc-first';
20844 ret[4].cls += ' fc-prev-last';
20845 ret[5].cls += ' fc-last';
20852 cls: 'fc-border-separate',
20853 style : 'width:100%',
20861 cls : 'fc-first fc-last',
20879 cls : 'fc-content',
20880 style : "position: relative;",
20883 cls : 'fc-view fc-view-month fc-grid',
20884 style : 'position: relative',
20885 unselectable : 'on',
20888 cls : 'fc-event-container',
20889 style : 'position:absolute;z-index:8;top:0;left:0;'
20907 initEvents : function()
20910 throw "can not find store for calendar";
20916 style: "text-align:center",
20920 style: "background-color:white;width:50%;margin:250 auto",
20924 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20935 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20937 var size = this.el.select('.fc-content', true).first().getSize();
20938 this.maskEl.setSize(size.width, size.height);
20939 this.maskEl.enableDisplayMode("block");
20940 if(!this.loadMask){
20941 this.maskEl.hide();
20944 this.store = Roo.factory(this.store, Roo.data);
20945 this.store.on('load', this.onLoad, this);
20946 this.store.on('beforeload', this.onBeforeLoad, this);
20950 this.cells = this.el.select('.fc-day',true);
20951 //Roo.log(this.cells);
20952 this.textNodes = this.el.query('.fc-day-number');
20953 this.cells.addClassOnOver('fc-state-hover');
20955 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20956 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20957 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20958 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20960 this.on('monthchange', this.onMonthChange, this);
20962 this.update(new Date().clearTime());
20965 resize : function() {
20966 var sz = this.el.getSize();
20968 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20969 this.el.select('.fc-day-content div',true).setHeight(34);
20974 showPrevMonth : function(e){
20975 this.update(this.activeDate.add("mo", -1));
20977 showToday : function(e){
20978 this.update(new Date().clearTime());
20981 showNextMonth : function(e){
20982 this.update(this.activeDate.add("mo", 1));
20986 showPrevYear : function(){
20987 this.update(this.activeDate.add("y", -1));
20991 showNextYear : function(){
20992 this.update(this.activeDate.add("y", 1));
20997 update : function(date)
20999 var vd = this.activeDate;
21000 this.activeDate = date;
21001 // if(vd && this.el){
21002 // var t = date.getTime();
21003 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21004 // Roo.log('using add remove');
21006 // this.fireEvent('monthchange', this, date);
21008 // this.cells.removeClass("fc-state-highlight");
21009 // this.cells.each(function(c){
21010 // if(c.dateValue == t){
21011 // c.addClass("fc-state-highlight");
21012 // setTimeout(function(){
21013 // try{c.dom.firstChild.focus();}catch(e){}
21023 var days = date.getDaysInMonth();
21025 var firstOfMonth = date.getFirstDateOfMonth();
21026 var startingPos = firstOfMonth.getDay()-this.startDay;
21028 if(startingPos < this.startDay){
21032 var pm = date.add(Date.MONTH, -1);
21033 var prevStart = pm.getDaysInMonth()-startingPos;
21035 this.cells = this.el.select('.fc-day',true);
21036 this.textNodes = this.el.query('.fc-day-number');
21037 this.cells.addClassOnOver('fc-state-hover');
21039 var cells = this.cells.elements;
21040 var textEls = this.textNodes;
21042 Roo.each(cells, function(cell){
21043 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21046 days += startingPos;
21048 // convert everything to numbers so it's fast
21049 var day = 86400000;
21050 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21053 //Roo.log(prevStart);
21055 var today = new Date().clearTime().getTime();
21056 var sel = date.clearTime().getTime();
21057 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21058 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21059 var ddMatch = this.disabledDatesRE;
21060 var ddText = this.disabledDatesText;
21061 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21062 var ddaysText = this.disabledDaysText;
21063 var format = this.format;
21065 var setCellClass = function(cal, cell){
21069 //Roo.log('set Cell Class');
21071 var t = d.getTime();
21075 cell.dateValue = t;
21077 cell.className += " fc-today";
21078 cell.className += " fc-state-highlight";
21079 cell.title = cal.todayText;
21082 // disable highlight in other month..
21083 //cell.className += " fc-state-highlight";
21088 cell.className = " fc-state-disabled";
21089 cell.title = cal.minText;
21093 cell.className = " fc-state-disabled";
21094 cell.title = cal.maxText;
21098 if(ddays.indexOf(d.getDay()) != -1){
21099 cell.title = ddaysText;
21100 cell.className = " fc-state-disabled";
21103 if(ddMatch && format){
21104 var fvalue = d.dateFormat(format);
21105 if(ddMatch.test(fvalue)){
21106 cell.title = ddText.replace("%0", fvalue);
21107 cell.className = " fc-state-disabled";
21111 if (!cell.initialClassName) {
21112 cell.initialClassName = cell.dom.className;
21115 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21120 for(; i < startingPos; i++) {
21121 textEls[i].innerHTML = (++prevStart);
21122 d.setDate(d.getDate()+1);
21124 cells[i].className = "fc-past fc-other-month";
21125 setCellClass(this, cells[i]);
21130 for(; i < days; i++){
21131 intDay = i - startingPos + 1;
21132 textEls[i].innerHTML = (intDay);
21133 d.setDate(d.getDate()+1);
21135 cells[i].className = ''; // "x-date-active";
21136 setCellClass(this, cells[i]);
21140 for(; i < 42; i++) {
21141 textEls[i].innerHTML = (++extraDays);
21142 d.setDate(d.getDate()+1);
21144 cells[i].className = "fc-future fc-other-month";
21145 setCellClass(this, cells[i]);
21148 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21150 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21152 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21153 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21155 if(totalRows != 6){
21156 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21157 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21160 this.fireEvent('monthchange', this, date);
21164 if(!this.internalRender){
21165 var main = this.el.dom.firstChild;
21166 var w = main.offsetWidth;
21167 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21168 Roo.fly(main).setWidth(w);
21169 this.internalRender = true;
21170 // opera does not respect the auto grow header center column
21171 // then, after it gets a width opera refuses to recalculate
21172 // without a second pass
21173 if(Roo.isOpera && !this.secondPass){
21174 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21175 this.secondPass = true;
21176 this.update.defer(10, this, [date]);
21183 findCell : function(dt) {
21184 dt = dt.clearTime().getTime();
21186 this.cells.each(function(c){
21187 //Roo.log("check " +c.dateValue + '?=' + dt);
21188 if(c.dateValue == dt){
21198 findCells : function(ev) {
21199 var s = ev.start.clone().clearTime().getTime();
21201 var e= ev.end.clone().clearTime().getTime();
21204 this.cells.each(function(c){
21205 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21207 if(c.dateValue > e){
21210 if(c.dateValue < s){
21219 // findBestRow: function(cells)
21223 // for (var i =0 ; i < cells.length;i++) {
21224 // ret = Math.max(cells[i].rows || 0,ret);
21231 addItem : function(ev)
21233 // look for vertical location slot in
21234 var cells = this.findCells(ev);
21236 // ev.row = this.findBestRow(cells);
21238 // work out the location.
21242 for(var i =0; i < cells.length; i++) {
21244 cells[i].row = cells[0].row;
21247 cells[i].row = cells[i].row + 1;
21257 if (crow.start.getY() == cells[i].getY()) {
21259 crow.end = cells[i];
21276 cells[0].events.push(ev);
21278 this.calevents.push(ev);
21281 clearEvents: function() {
21283 if(!this.calevents){
21287 Roo.each(this.cells.elements, function(c){
21293 Roo.each(this.calevents, function(e) {
21294 Roo.each(e.els, function(el) {
21295 el.un('mouseenter' ,this.onEventEnter, this);
21296 el.un('mouseleave' ,this.onEventLeave, this);
21301 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21307 renderEvents: function()
21311 this.cells.each(function(c) {
21320 if(c.row != c.events.length){
21321 r = 4 - (4 - (c.row - c.events.length));
21324 c.events = ev.slice(0, r);
21325 c.more = ev.slice(r);
21327 if(c.more.length && c.more.length == 1){
21328 c.events.push(c.more.pop());
21331 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21335 this.cells.each(function(c) {
21337 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21340 for (var e = 0; e < c.events.length; e++){
21341 var ev = c.events[e];
21342 var rows = ev.rows;
21344 for(var i = 0; i < rows.length; i++) {
21346 // how many rows should it span..
21349 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21350 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21352 unselectable : "on",
21355 cls: 'fc-event-inner',
21359 // cls: 'fc-event-time',
21360 // html : cells.length > 1 ? '' : ev.time
21364 cls: 'fc-event-title',
21365 html : String.format('{0}', ev.title)
21372 cls: 'ui-resizable-handle ui-resizable-e',
21373 html : '  '
21380 cfg.cls += ' fc-event-start';
21382 if ((i+1) == rows.length) {
21383 cfg.cls += ' fc-event-end';
21386 var ctr = _this.el.select('.fc-event-container',true).first();
21387 var cg = ctr.createChild(cfg);
21389 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21390 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21392 var r = (c.more.length) ? 1 : 0;
21393 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21394 cg.setWidth(ebox.right - sbox.x -2);
21396 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21397 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21398 cg.on('click', _this.onEventClick, _this, ev);
21409 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21410 style : 'position: absolute',
21411 unselectable : "on",
21414 cls: 'fc-event-inner',
21418 cls: 'fc-event-title',
21426 cls: 'ui-resizable-handle ui-resizable-e',
21427 html : '  '
21433 var ctr = _this.el.select('.fc-event-container',true).first();
21434 var cg = ctr.createChild(cfg);
21436 var sbox = c.select('.fc-day-content',true).first().getBox();
21437 var ebox = c.select('.fc-day-content',true).first().getBox();
21439 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21440 cg.setWidth(ebox.right - sbox.x -2);
21442 cg.on('click', _this.onMoreEventClick, _this, c.more);
21452 onEventEnter: function (e, el,event,d) {
21453 this.fireEvent('evententer', this, el, event);
21456 onEventLeave: function (e, el,event,d) {
21457 this.fireEvent('eventleave', this, el, event);
21460 onEventClick: function (e, el,event,d) {
21461 this.fireEvent('eventclick', this, el, event);
21464 onMonthChange: function () {
21468 onMoreEventClick: function(e, el, more)
21472 this.calpopover.placement = 'right';
21473 this.calpopover.setTitle('More');
21475 this.calpopover.setContent('');
21477 var ctr = this.calpopover.el.select('.popover-content', true).first();
21479 Roo.each(more, function(m){
21481 cls : 'fc-event-hori fc-event-draggable',
21484 var cg = ctr.createChild(cfg);
21486 cg.on('click', _this.onEventClick, _this, m);
21489 this.calpopover.show(el);
21494 onLoad: function ()
21496 this.calevents = [];
21499 if(this.store.getCount() > 0){
21500 this.store.data.each(function(d){
21503 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21504 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21505 time : d.data.start_time,
21506 title : d.data.title,
21507 description : d.data.description,
21508 venue : d.data.venue
21513 this.renderEvents();
21515 if(this.calevents.length && this.loadMask){
21516 this.maskEl.hide();
21520 onBeforeLoad: function()
21522 this.clearEvents();
21524 this.maskEl.show();
21538 * @class Roo.bootstrap.Popover
21539 * @extends Roo.bootstrap.Component
21540 * @parent none builder
21541 * @children Roo.bootstrap.Component
21542 * Bootstrap Popover class
21543 * @cfg {String} html contents of the popover (or false to use children..)
21544 * @cfg {String} title of popover (or false to hide)
21545 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21546 * @cfg {String} trigger click || hover (or false to trigger manually)
21547 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21548 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21549 * - if false and it has a 'parent' then it will be automatically added to that element
21550 * - if string - Roo.get will be called
21551 * @cfg {Number} delay - delay before showing
21554 * Create a new Popover
21555 * @param {Object} config The config object
21558 Roo.bootstrap.Popover = function(config){
21559 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21565 * After the popover show
21567 * @param {Roo.bootstrap.Popover} this
21572 * After the popover hide
21574 * @param {Roo.bootstrap.Popover} this
21580 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21585 placement : 'right',
21586 trigger : 'hover', // hover
21592 can_build_overlaid : false,
21594 maskEl : false, // the mask element
21597 alignEl : false, // when show is called with an element - this get's stored.
21599 getChildContainer : function()
21601 return this.contentEl;
21604 getPopoverHeader : function()
21606 this.title = true; // flag not to hide it..
21607 this.headerEl.addClass('p-0');
21608 return this.headerEl
21612 getAutoCreate : function(){
21615 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21616 style: 'display:block',
21622 cls : 'popover-inner ',
21626 cls: 'popover-title popover-header',
21627 html : this.title === false ? '' : this.title
21630 cls : 'popover-content popover-body ' + (this.cls || ''),
21631 html : this.html || ''
21642 * @param {string} the title
21644 setTitle: function(str)
21648 this.headerEl.dom.innerHTML = str;
21653 * @param {string} the body content
21655 setContent: function(str)
21658 if (this.contentEl) {
21659 this.contentEl.dom.innerHTML = str;
21663 // as it get's added to the bottom of the page.
21664 onRender : function(ct, position)
21666 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21671 var cfg = Roo.apply({}, this.getAutoCreate());
21675 cfg.cls += ' ' + this.cls;
21678 cfg.style = this.style;
21680 //Roo.log("adding to ");
21681 this.el = Roo.get(document.body).createChild(cfg, position);
21682 // Roo.log(this.el);
21685 this.contentEl = this.el.select('.popover-content',true).first();
21686 this.headerEl = this.el.select('.popover-title',true).first();
21689 if(typeof(this.items) != 'undefined'){
21690 var items = this.items;
21693 for(var i =0;i < items.length;i++) {
21694 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21698 this.items = nitems;
21700 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21701 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21708 resizeMask : function()
21710 this.maskEl.setSize(
21711 Roo.lib.Dom.getViewWidth(true),
21712 Roo.lib.Dom.getViewHeight(true)
21716 initEvents : function()
21720 Roo.bootstrap.Popover.register(this);
21723 this.arrowEl = this.el.select('.arrow',true).first();
21724 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21725 this.el.enableDisplayMode('block');
21729 if (this.over === false && !this.parent()) {
21732 if (this.triggers === false) {
21737 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21738 var triggers = this.trigger ? this.trigger.split(' ') : [];
21739 Roo.each(triggers, function(trigger) {
21741 if (trigger == 'click') {
21742 on_el.on('click', this.toggle, this);
21743 } else if (trigger != 'manual') {
21744 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21745 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21747 on_el.on(eventIn ,this.enter, this);
21748 on_el.on(eventOut, this.leave, this);
21758 toggle : function () {
21759 this.hoverState == 'in' ? this.leave() : this.enter();
21762 enter : function () {
21764 clearTimeout(this.timeout);
21766 this.hoverState = 'in';
21768 if (!this.delay || !this.delay.show) {
21773 this.timeout = setTimeout(function () {
21774 if (_t.hoverState == 'in') {
21777 }, this.delay.show)
21780 leave : function() {
21781 clearTimeout(this.timeout);
21783 this.hoverState = 'out';
21785 if (!this.delay || !this.delay.hide) {
21790 this.timeout = setTimeout(function () {
21791 if (_t.hoverState == 'out') {
21794 }, this.delay.hide)
21798 * update the position of the dialog
21799 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21804 doAlign : function()
21807 if (this.alignEl) {
21808 this.updatePosition(this.placement, true);
21811 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21812 var es = this.el.getSize();
21813 var x = Roo.lib.Dom.getViewWidth()/2;
21814 var y = Roo.lib.Dom.getViewHeight()/2;
21815 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21827 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21828 * @param {string} (left|right|top|bottom) position
21830 show : function (on_el, placement)
21832 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21833 on_el = on_el || false; // default to false
21836 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21837 on_el = this.parent().el;
21838 } else if (this.over) {
21839 on_el = Roo.get(this.over);
21844 this.alignEl = Roo.get( on_el );
21847 this.render(document.body);
21853 if (this.title === false) {
21854 this.headerEl.hide();
21859 this.el.dom.style.display = 'block';
21863 //var arrow = this.el.select('.arrow',true).first();
21864 //arrow.set(align[2],
21866 this.el.addClass('in');
21870 this.hoverState = 'in';
21873 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21874 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21875 this.maskEl.dom.style.display = 'block';
21876 this.maskEl.addClass('show');
21878 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21880 this.fireEvent('show', this);
21884 * fire this manually after loading a grid in the table for example
21885 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21886 * @param {Boolean} try and move it if we cant get right position.
21888 updatePosition : function(placement, try_move)
21890 // allow for calling with no parameters
21891 placement = placement ? placement : this.placement;
21892 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21894 this.el.removeClass([
21895 'fade','top','bottom', 'left', 'right','in',
21896 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21898 this.el.addClass(placement + ' bs-popover-' + placement);
21900 if (!this.alignEl ) {
21904 switch (placement) {
21906 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21907 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21908 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21909 //normal display... or moved up/down.
21910 this.el.setXY(offset);
21911 var xy = this.alignEl.getAnchorXY('tr', false);
21913 this.arrowEl.setXY(xy);
21916 // continue through...
21917 return this.updatePosition('left', false);
21921 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21922 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21923 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21924 //normal display... or moved up/down.
21925 this.el.setXY(offset);
21926 var xy = this.alignEl.getAnchorXY('tl', false);
21927 xy[0]-=10;xy[1]+=5; // << fix me
21928 this.arrowEl.setXY(xy);
21932 return this.updatePosition('right', false);
21935 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21936 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21937 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21938 //normal display... or moved up/down.
21939 this.el.setXY(offset);
21940 var xy = this.alignEl.getAnchorXY('t', false);
21941 xy[1]-=10; // << fix me
21942 this.arrowEl.setXY(xy);
21946 return this.updatePosition('bottom', false);
21949 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21950 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21951 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21952 //normal display... or moved up/down.
21953 this.el.setXY(offset);
21954 var xy = this.alignEl.getAnchorXY('b', false);
21955 xy[1]+=2; // << fix me
21956 this.arrowEl.setXY(xy);
21960 return this.updatePosition('top', false);
21971 this.el.setXY([0,0]);
21972 this.el.removeClass('in');
21974 this.hoverState = null;
21975 this.maskEl.hide(); // always..
21976 this.fireEvent('hide', this);
21982 Roo.apply(Roo.bootstrap.Popover, {
21985 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21986 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21987 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21988 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21993 clickHander : false,
21997 onMouseDown : function(e)
21999 if (this.popups.length && !e.getTarget(".roo-popover")) {
22000 /// what is nothing is showing..
22009 register : function(popup)
22011 if (!Roo.bootstrap.Popover.clickHandler) {
22012 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22014 // hide other popups.
22015 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22016 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22017 this.hideAll(); //<< why?
22018 //this.popups.push(popup);
22020 hideAll : function()
22022 this.popups.forEach(function(p) {
22026 onShow : function() {
22027 Roo.bootstrap.Popover.popups.push(this);
22029 onHide : function() {
22030 Roo.bootstrap.Popover.popups.remove(this);
22035 * @class Roo.bootstrap.PopoverNav
22036 * @extends Roo.bootstrap.nav.Simplebar
22037 * @parent Roo.bootstrap.Popover
22038 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22040 * Bootstrap Popover header navigation class
22041 * FIXME? should this go under nav?
22045 * Create a new Popover Header Navigation
22046 * @param {Object} config The config object
22049 Roo.bootstrap.PopoverNav = function(config){
22050 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22053 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22056 container_method : 'getPopoverHeader'
22074 * @class Roo.bootstrap.Progress
22075 * @extends Roo.bootstrap.Component
22076 * @children Roo.bootstrap.ProgressBar
22077 * Bootstrap Progress class
22078 * @cfg {Boolean} striped striped of the progress bar
22079 * @cfg {Boolean} active animated of the progress bar
22083 * Create a new Progress
22084 * @param {Object} config The config object
22087 Roo.bootstrap.Progress = function(config){
22088 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22091 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22096 getAutoCreate : function(){
22104 cfg.cls += ' progress-striped';
22108 cfg.cls += ' active';
22127 * @class Roo.bootstrap.ProgressBar
22128 * @extends Roo.bootstrap.Component
22129 * Bootstrap ProgressBar class
22130 * @cfg {Number} aria_valuenow aria-value now
22131 * @cfg {Number} aria_valuemin aria-value min
22132 * @cfg {Number} aria_valuemax aria-value max
22133 * @cfg {String} label label for the progress bar
22134 * @cfg {String} panel (success | info | warning | danger )
22135 * @cfg {String} role role of the progress bar
22136 * @cfg {String} sr_only text
22140 * Create a new ProgressBar
22141 * @param {Object} config The config object
22144 Roo.bootstrap.ProgressBar = function(config){
22145 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22148 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22152 aria_valuemax : 100,
22158 getAutoCreate : function()
22163 cls: 'progress-bar',
22164 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22176 cfg.role = this.role;
22179 if(this.aria_valuenow){
22180 cfg['aria-valuenow'] = this.aria_valuenow;
22183 if(this.aria_valuemin){
22184 cfg['aria-valuemin'] = this.aria_valuemin;
22187 if(this.aria_valuemax){
22188 cfg['aria-valuemax'] = this.aria_valuemax;
22191 if(this.label && !this.sr_only){
22192 cfg.html = this.label;
22196 cfg.cls += ' progress-bar-' + this.panel;
22202 update : function(aria_valuenow)
22204 this.aria_valuenow = aria_valuenow;
22206 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22214 * @class Roo.bootstrap.TabGroup
22215 * @extends Roo.bootstrap.Column
22216 * @children Roo.bootstrap.TabPanel
22217 * Bootstrap Column class
22218 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22219 * @cfg {Boolean} carousel true to make the group behave like a carousel
22220 * @cfg {Boolean} bullets show bullets for the panels
22221 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22222 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22223 * @cfg {Boolean} showarrow (true|false) show arrow default true
22226 * Create a new TabGroup
22227 * @param {Object} config The config object
22230 Roo.bootstrap.TabGroup = function(config){
22231 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22233 this.navId = Roo.id();
22236 Roo.bootstrap.TabGroup.register(this);
22240 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22243 transition : false,
22248 slideOnTouch : false,
22251 getAutoCreate : function()
22253 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22255 cfg.cls += ' tab-content';
22257 if (this.carousel) {
22258 cfg.cls += ' carousel slide';
22261 cls : 'carousel-inner',
22265 if(this.bullets && !Roo.isTouch){
22268 cls : 'carousel-bullets',
22272 if(this.bullets_cls){
22273 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22280 cfg.cn[0].cn.push(bullets);
22283 if(this.showarrow){
22284 cfg.cn[0].cn.push({
22286 class : 'carousel-arrow',
22290 class : 'carousel-prev',
22294 class : 'fa fa-chevron-left'
22300 class : 'carousel-next',
22304 class : 'fa fa-chevron-right'
22317 initEvents: function()
22319 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22320 // this.el.on("touchstart", this.onTouchStart, this);
22323 if(this.autoslide){
22326 this.slideFn = window.setInterval(function() {
22327 _this.showPanelNext();
22331 if(this.showarrow){
22332 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22333 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22339 // onTouchStart : function(e, el, o)
22341 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22345 // this.showPanelNext();
22349 getChildContainer : function()
22351 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22355 * register a Navigation item
22356 * @param {Roo.bootstrap.nav.Item} the navitem to add
22358 register : function(item)
22360 this.tabs.push( item);
22361 item.navId = this.navId; // not really needed..
22366 getActivePanel : function()
22369 Roo.each(this.tabs, function(t) {
22379 getPanelByName : function(n)
22382 Roo.each(this.tabs, function(t) {
22383 if (t.tabId == n) {
22391 indexOfPanel : function(p)
22394 Roo.each(this.tabs, function(t,i) {
22395 if (t.tabId == p.tabId) {
22404 * show a specific panel
22405 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22406 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22408 showPanel : function (pan)
22410 if(this.transition || typeof(pan) == 'undefined'){
22411 Roo.log("waiting for the transitionend");
22415 if (typeof(pan) == 'number') {
22416 pan = this.tabs[pan];
22419 if (typeof(pan) == 'string') {
22420 pan = this.getPanelByName(pan);
22423 var cur = this.getActivePanel();
22426 Roo.log('pan or acitve pan is undefined');
22430 if (pan.tabId == this.getActivePanel().tabId) {
22434 if (false === cur.fireEvent('beforedeactivate')) {
22438 if(this.bullets > 0 && !Roo.isTouch){
22439 this.setActiveBullet(this.indexOfPanel(pan));
22442 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22444 //class="carousel-item carousel-item-next carousel-item-left"
22446 this.transition = true;
22447 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22448 var lr = dir == 'next' ? 'left' : 'right';
22449 pan.el.addClass(dir); // or prev
22450 pan.el.addClass('carousel-item-' + dir); // or prev
22451 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22452 cur.el.addClass(lr); // or right
22453 pan.el.addClass(lr);
22454 cur.el.addClass('carousel-item-' +lr); // or right
22455 pan.el.addClass('carousel-item-' +lr);
22459 cur.el.on('transitionend', function() {
22460 Roo.log("trans end?");
22462 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22463 pan.setActive(true);
22465 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22466 cur.setActive(false);
22468 _this.transition = false;
22470 }, this, { single: true } );
22475 cur.setActive(false);
22476 pan.setActive(true);
22481 showPanelNext : function()
22483 var i = this.indexOfPanel(this.getActivePanel());
22485 if (i >= this.tabs.length - 1 && !this.autoslide) {
22489 if (i >= this.tabs.length - 1 && this.autoslide) {
22493 this.showPanel(this.tabs[i+1]);
22496 showPanelPrev : function()
22498 var i = this.indexOfPanel(this.getActivePanel());
22500 if (i < 1 && !this.autoslide) {
22504 if (i < 1 && this.autoslide) {
22505 i = this.tabs.length;
22508 this.showPanel(this.tabs[i-1]);
22512 addBullet: function()
22514 if(!this.bullets || Roo.isTouch){
22517 var ctr = this.el.select('.carousel-bullets',true).first();
22518 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22519 var bullet = ctr.createChild({
22520 cls : 'bullet bullet-' + i
22521 },ctr.dom.lastChild);
22526 bullet.on('click', (function(e, el, o, ii, t){
22528 e.preventDefault();
22530 this.showPanel(ii);
22532 if(this.autoslide && this.slideFn){
22533 clearInterval(this.slideFn);
22534 this.slideFn = window.setInterval(function() {
22535 _this.showPanelNext();
22539 }).createDelegate(this, [i, bullet], true));
22544 setActiveBullet : function(i)
22550 Roo.each(this.el.select('.bullet', true).elements, function(el){
22551 el.removeClass('selected');
22554 var bullet = this.el.select('.bullet-' + i, true).first();
22560 bullet.addClass('selected');
22571 Roo.apply(Roo.bootstrap.TabGroup, {
22575 * register a Navigation Group
22576 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22578 register : function(navgrp)
22580 this.groups[navgrp.navId] = navgrp;
22584 * fetch a Navigation Group based on the navigation ID
22585 * if one does not exist , it will get created.
22586 * @param {string} the navgroup to add
22587 * @returns {Roo.bootstrap.nav.Group} the navgroup
22589 get: function(navId) {
22590 if (typeof(this.groups[navId]) == 'undefined') {
22591 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22593 return this.groups[navId] ;
22608 * @class Roo.bootstrap.TabPanel
22609 * @extends Roo.bootstrap.Component
22610 * @children Roo.bootstrap.Component
22611 * Bootstrap TabPanel class
22612 * @cfg {Boolean} active panel active
22613 * @cfg {String} html panel content
22614 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22615 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22616 * @cfg {String} href click to link..
22617 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22621 * Create a new TabPanel
22622 * @param {Object} config The config object
22625 Roo.bootstrap.TabPanel = function(config){
22626 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22630 * Fires when the active status changes
22631 * @param {Roo.bootstrap.TabPanel} this
22632 * @param {Boolean} state the new state
22637 * @event beforedeactivate
22638 * Fires before a tab is de-activated - can be used to do validation on a form.
22639 * @param {Roo.bootstrap.TabPanel} this
22640 * @return {Boolean} false if there is an error
22643 'beforedeactivate': true
22646 this.tabId = this.tabId || Roo.id();
22650 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22657 touchSlide : false,
22658 getAutoCreate : function(){
22663 // item is needed for carousel - not sure if it has any effect otherwise
22664 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22665 html: this.html || ''
22669 cfg.cls += ' active';
22673 cfg.tabId = this.tabId;
22681 initEvents: function()
22683 var p = this.parent();
22685 this.navId = this.navId || p.navId;
22687 if (typeof(this.navId) != 'undefined') {
22688 // not really needed.. but just in case.. parent should be a NavGroup.
22689 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22693 var i = tg.tabs.length - 1;
22695 if(this.active && tg.bullets > 0 && i < tg.bullets){
22696 tg.setActiveBullet(i);
22700 this.el.on('click', this.onClick, this);
22702 if(Roo.isTouch && this.touchSlide){
22703 this.el.on("touchstart", this.onTouchStart, this);
22704 this.el.on("touchmove", this.onTouchMove, this);
22705 this.el.on("touchend", this.onTouchEnd, this);
22710 onRender : function(ct, position)
22712 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22715 setActive : function(state)
22717 Roo.log("panel - set active " + this.tabId + "=" + state);
22719 this.active = state;
22721 this.el.removeClass('active');
22723 } else if (!this.el.hasClass('active')) {
22724 this.el.addClass('active');
22727 this.fireEvent('changed', this, state);
22730 onClick : function(e)
22732 e.preventDefault();
22734 if(!this.href.length){
22738 window.location.href = this.href;
22747 onTouchStart : function(e)
22749 this.swiping = false;
22751 this.startX = e.browserEvent.touches[0].clientX;
22752 this.startY = e.browserEvent.touches[0].clientY;
22755 onTouchMove : function(e)
22757 this.swiping = true;
22759 this.endX = e.browserEvent.touches[0].clientX;
22760 this.endY = e.browserEvent.touches[0].clientY;
22763 onTouchEnd : function(e)
22770 var tabGroup = this.parent();
22772 if(this.endX > this.startX){ // swiping right
22773 tabGroup.showPanelPrev();
22777 if(this.startX > this.endX){ // swiping left
22778 tabGroup.showPanelNext();
22797 * @class Roo.bootstrap.form.DateField
22798 * @extends Roo.bootstrap.form.Input
22799 * Bootstrap DateField class
22800 * @cfg {Number} weekStart default 0
22801 * @cfg {String} viewMode default empty, (months|years)
22802 * @cfg {String} minViewMode default empty, (months|years)
22803 * @cfg {Number} startDate default -Infinity
22804 * @cfg {Number} endDate default Infinity
22805 * @cfg {Boolean} todayHighlight default false
22806 * @cfg {Boolean} todayBtn default false
22807 * @cfg {Boolean} calendarWeeks default false
22808 * @cfg {Object} daysOfWeekDisabled default empty
22809 * @cfg {Boolean} singleMode default false (true | false)
22811 * @cfg {Boolean} keyboardNavigation default true
22812 * @cfg {String} language default en
22815 * Create a new DateField
22816 * @param {Object} config The config object
22819 Roo.bootstrap.form.DateField = function(config){
22820 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22824 * Fires when this field show.
22825 * @param {Roo.bootstrap.form.DateField} this
22826 * @param {Mixed} date The date value
22831 * Fires when this field hide.
22832 * @param {Roo.bootstrap.form.DateField} this
22833 * @param {Mixed} date The date value
22838 * Fires when select a date.
22839 * @param {Roo.bootstrap.form.DateField} this
22840 * @param {Mixed} date The date value
22844 * @event beforeselect
22845 * Fires when before select a date.
22846 * @param {Roo.bootstrap.form.DateField} this
22847 * @param {Mixed} date The date value
22849 beforeselect : true
22853 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22856 * @cfg {String} format
22857 * The default date format string which can be overriden for localization support. The format must be
22858 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22862 * @cfg {String} altFormats
22863 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22864 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22866 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22874 todayHighlight : false,
22880 keyboardNavigation: true,
22882 calendarWeeks: false,
22884 startDate: -Infinity,
22888 daysOfWeekDisabled: [],
22892 singleMode : false,
22894 UTCDate: function()
22896 return new Date(Date.UTC.apply(Date, arguments));
22899 UTCToday: function()
22901 var today = new Date();
22902 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22905 getDate: function() {
22906 var d = this.getUTCDate();
22907 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22910 getUTCDate: function() {
22914 setDate: function(d) {
22915 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22918 setUTCDate: function(d) {
22920 this.setValue(this.formatDate(this.date));
22923 onRender: function(ct, position)
22926 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22928 this.language = this.language || 'en';
22929 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22930 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22932 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22933 this.format = this.format || 'm/d/y';
22934 this.isInline = false;
22935 this.isInput = true;
22936 this.component = this.el.select('.add-on', true).first() || false;
22937 this.component = (this.component && this.component.length === 0) ? false : this.component;
22938 this.hasInput = this.component && this.inputEl().length;
22940 if (typeof(this.minViewMode === 'string')) {
22941 switch (this.minViewMode) {
22943 this.minViewMode = 1;
22946 this.minViewMode = 2;
22949 this.minViewMode = 0;
22954 if (typeof(this.viewMode === 'string')) {
22955 switch (this.viewMode) {
22968 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22970 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22972 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22974 this.picker().on('mousedown', this.onMousedown, this);
22975 this.picker().on('click', this.onClick, this);
22977 this.picker().addClass('datepicker-dropdown');
22979 this.startViewMode = this.viewMode;
22981 if(this.singleMode){
22982 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22983 v.setVisibilityMode(Roo.Element.DISPLAY);
22987 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22988 v.setStyle('width', '189px');
22992 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22993 if(!this.calendarWeeks){
22998 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22999 v.attr('colspan', function(i, val){
23000 return parseInt(val) + 1;
23005 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23007 this.setStartDate(this.startDate);
23008 this.setEndDate(this.endDate);
23010 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23017 if(this.isInline) {
23022 picker : function()
23024 return this.pickerEl;
23025 // return this.el.select('.datepicker', true).first();
23028 fillDow: function()
23030 var dowCnt = this.weekStart;
23039 if(this.calendarWeeks){
23047 while (dowCnt < this.weekStart + 7) {
23051 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23055 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23058 fillMonths: function()
23061 var months = this.picker().select('>.datepicker-months td', true).first();
23063 months.dom.innerHTML = '';
23069 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23072 months.createChild(month);
23079 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;
23081 if (this.date < this.startDate) {
23082 this.viewDate = new Date(this.startDate);
23083 } else if (this.date > this.endDate) {
23084 this.viewDate = new Date(this.endDate);
23086 this.viewDate = new Date(this.date);
23094 var d = new Date(this.viewDate),
23095 year = d.getUTCFullYear(),
23096 month = d.getUTCMonth(),
23097 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23098 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23099 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23100 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23101 currentDate = this.date && this.date.valueOf(),
23102 today = this.UTCToday();
23104 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23106 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23108 // this.picker.select('>tfoot th.today').
23109 // .text(dates[this.language].today)
23110 // .toggle(this.todayBtn !== false);
23112 this.updateNavArrows();
23115 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23117 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23119 prevMonth.setUTCDate(day);
23121 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23123 var nextMonth = new Date(prevMonth);
23125 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23127 nextMonth = nextMonth.valueOf();
23129 var fillMonths = false;
23131 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23133 while(prevMonth.valueOf() <= nextMonth) {
23136 if (prevMonth.getUTCDay() === this.weekStart) {
23138 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23146 if(this.calendarWeeks){
23147 // ISO 8601: First week contains first thursday.
23148 // ISO also states week starts on Monday, but we can be more abstract here.
23150 // Start of current week: based on weekstart/current date
23151 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23152 // Thursday of this week
23153 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23154 // First Thursday of year, year from thursday
23155 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23156 // Calendar week: ms between thursdays, div ms per day, div 7 days
23157 calWeek = (th - yth) / 864e5 / 7 + 1;
23159 fillMonths.cn.push({
23167 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23169 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23172 if (this.todayHighlight &&
23173 prevMonth.getUTCFullYear() == today.getFullYear() &&
23174 prevMonth.getUTCMonth() == today.getMonth() &&
23175 prevMonth.getUTCDate() == today.getDate()) {
23176 clsName += ' today';
23179 if (currentDate && prevMonth.valueOf() === currentDate) {
23180 clsName += ' active';
23183 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23184 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23185 clsName += ' disabled';
23188 fillMonths.cn.push({
23190 cls: 'day ' + clsName,
23191 html: prevMonth.getDate()
23194 prevMonth.setDate(prevMonth.getDate()+1);
23197 var currentYear = this.date && this.date.getUTCFullYear();
23198 var currentMonth = this.date && this.date.getUTCMonth();
23200 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23202 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23203 v.removeClass('active');
23205 if(currentYear === year && k === currentMonth){
23206 v.addClass('active');
23209 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23210 v.addClass('disabled');
23216 year = parseInt(year/10, 10) * 10;
23218 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23220 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23223 for (var i = -1; i < 11; i++) {
23224 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23226 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23234 showMode: function(dir)
23237 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23240 Roo.each(this.picker().select('>div',true).elements, function(v){
23241 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23244 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23249 if(this.isInline) {
23253 this.picker().removeClass(['bottom', 'top']);
23255 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23257 * place to the top of element!
23261 this.picker().addClass('top');
23262 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23267 this.picker().addClass('bottom');
23269 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23272 parseDate : function(value)
23274 if(!value || value instanceof Date){
23277 var v = Date.parseDate(value, this.format);
23278 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23279 v = Date.parseDate(value, 'Y-m-d');
23281 if(!v && this.altFormats){
23282 if(!this.altFormatsArray){
23283 this.altFormatsArray = this.altFormats.split("|");
23285 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23286 v = Date.parseDate(value, this.altFormatsArray[i]);
23292 formatDate : function(date, fmt)
23294 return (!date || !(date instanceof Date)) ?
23295 date : date.dateFormat(fmt || this.format);
23298 onFocus : function()
23300 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23304 onBlur : function()
23306 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23308 var d = this.inputEl().getValue();
23315 showPopup : function()
23317 this.picker().show();
23321 this.fireEvent('showpopup', this, this.date);
23324 hidePopup : function()
23326 if(this.isInline) {
23329 this.picker().hide();
23330 this.viewMode = this.startViewMode;
23333 this.fireEvent('hidepopup', this, this.date);
23337 onMousedown: function(e)
23339 e.stopPropagation();
23340 e.preventDefault();
23345 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23349 setValue: function(v)
23351 if(this.fireEvent('beforeselect', this, v) !== false){
23352 var d = new Date(this.parseDate(v) ).clearTime();
23354 if(isNaN(d.getTime())){
23355 this.date = this.viewDate = '';
23356 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23360 v = this.formatDate(d);
23362 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23364 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23368 this.fireEvent('select', this, this.date);
23372 getValue: function()
23374 return this.formatDate(this.date);
23377 fireKey: function(e)
23379 if (!this.picker().isVisible()){
23380 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23386 var dateChanged = false,
23388 newDate, newViewDate;
23393 e.preventDefault();
23397 if (!this.keyboardNavigation) {
23400 dir = e.keyCode == 37 ? -1 : 1;
23403 newDate = this.moveYear(this.date, dir);
23404 newViewDate = this.moveYear(this.viewDate, dir);
23405 } else if (e.shiftKey){
23406 newDate = this.moveMonth(this.date, dir);
23407 newViewDate = this.moveMonth(this.viewDate, dir);
23409 newDate = new Date(this.date);
23410 newDate.setUTCDate(this.date.getUTCDate() + dir);
23411 newViewDate = new Date(this.viewDate);
23412 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23414 if (this.dateWithinRange(newDate)){
23415 this.date = newDate;
23416 this.viewDate = newViewDate;
23417 this.setValue(this.formatDate(this.date));
23419 e.preventDefault();
23420 dateChanged = true;
23425 if (!this.keyboardNavigation) {
23428 dir = e.keyCode == 38 ? -1 : 1;
23430 newDate = this.moveYear(this.date, dir);
23431 newViewDate = this.moveYear(this.viewDate, dir);
23432 } else if (e.shiftKey){
23433 newDate = this.moveMonth(this.date, dir);
23434 newViewDate = this.moveMonth(this.viewDate, dir);
23436 newDate = new Date(this.date);
23437 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23438 newViewDate = new Date(this.viewDate);
23439 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23441 if (this.dateWithinRange(newDate)){
23442 this.date = newDate;
23443 this.viewDate = newViewDate;
23444 this.setValue(this.formatDate(this.date));
23446 e.preventDefault();
23447 dateChanged = true;
23451 this.setValue(this.formatDate(this.date));
23453 e.preventDefault();
23456 this.setValue(this.formatDate(this.date));
23470 onClick: function(e)
23472 e.stopPropagation();
23473 e.preventDefault();
23475 var target = e.getTarget();
23477 if(target.nodeName.toLowerCase() === 'i'){
23478 target = Roo.get(target).dom.parentNode;
23481 var nodeName = target.nodeName;
23482 var className = target.className;
23483 var html = target.innerHTML;
23484 //Roo.log(nodeName);
23486 switch(nodeName.toLowerCase()) {
23488 switch(className) {
23494 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23495 switch(this.viewMode){
23497 this.viewDate = this.moveMonth(this.viewDate, dir);
23501 this.viewDate = this.moveYear(this.viewDate, dir);
23507 var date = new Date();
23508 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23510 this.setValue(this.formatDate(this.date));
23517 if (className.indexOf('disabled') < 0) {
23518 if (!this.viewDate) {
23519 this.viewDate = new Date();
23521 this.viewDate.setUTCDate(1);
23522 if (className.indexOf('month') > -1) {
23523 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23525 var year = parseInt(html, 10) || 0;
23526 this.viewDate.setUTCFullYear(year);
23530 if(this.singleMode){
23531 this.setValue(this.formatDate(this.viewDate));
23542 //Roo.log(className);
23543 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23544 var day = parseInt(html, 10) || 1;
23545 var year = (this.viewDate || new Date()).getUTCFullYear(),
23546 month = (this.viewDate || new Date()).getUTCMonth();
23548 if (className.indexOf('old') > -1) {
23555 } else if (className.indexOf('new') > -1) {
23563 //Roo.log([year,month,day]);
23564 this.date = this.UTCDate(year, month, day,0,0,0,0);
23565 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23567 //Roo.log(this.formatDate(this.date));
23568 this.setValue(this.formatDate(this.date));
23575 setStartDate: function(startDate)
23577 this.startDate = startDate || -Infinity;
23578 if (this.startDate !== -Infinity) {
23579 this.startDate = this.parseDate(this.startDate);
23582 this.updateNavArrows();
23585 setEndDate: function(endDate)
23587 this.endDate = endDate || Infinity;
23588 if (this.endDate !== Infinity) {
23589 this.endDate = this.parseDate(this.endDate);
23592 this.updateNavArrows();
23595 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23597 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23598 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23599 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23601 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23602 return parseInt(d, 10);
23605 this.updateNavArrows();
23608 updateNavArrows: function()
23610 if(this.singleMode){
23614 var d = new Date(this.viewDate),
23615 year = d.getUTCFullYear(),
23616 month = d.getUTCMonth();
23618 Roo.each(this.picker().select('.prev', true).elements, function(v){
23620 switch (this.viewMode) {
23623 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23629 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23636 Roo.each(this.picker().select('.next', true).elements, function(v){
23638 switch (this.viewMode) {
23641 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23647 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23655 moveMonth: function(date, dir)
23660 var new_date = new Date(date.valueOf()),
23661 day = new_date.getUTCDate(),
23662 month = new_date.getUTCMonth(),
23663 mag = Math.abs(dir),
23665 dir = dir > 0 ? 1 : -1;
23668 // If going back one month, make sure month is not current month
23669 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23671 return new_date.getUTCMonth() == month;
23673 // If going forward one month, make sure month is as expected
23674 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23676 return new_date.getUTCMonth() != new_month;
23678 new_month = month + dir;
23679 new_date.setUTCMonth(new_month);
23680 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23681 if (new_month < 0 || new_month > 11) {
23682 new_month = (new_month + 12) % 12;
23685 // For magnitudes >1, move one month at a time...
23686 for (var i=0; i<mag; i++) {
23687 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23688 new_date = this.moveMonth(new_date, dir);
23690 // ...then reset the day, keeping it in the new month
23691 new_month = new_date.getUTCMonth();
23692 new_date.setUTCDate(day);
23694 return new_month != new_date.getUTCMonth();
23697 // Common date-resetting loop -- if date is beyond end of month, make it
23700 new_date.setUTCDate(--day);
23701 new_date.setUTCMonth(new_month);
23706 moveYear: function(date, dir)
23708 return this.moveMonth(date, dir*12);
23711 dateWithinRange: function(date)
23713 return date >= this.startDate && date <= this.endDate;
23719 this.picker().remove();
23722 validateValue : function(value)
23724 if(this.getVisibilityEl().hasClass('hidden')){
23728 if(value.length < 1) {
23729 if(this.allowBlank){
23735 if(value.length < this.minLength){
23738 if(value.length > this.maxLength){
23742 var vt = Roo.form.VTypes;
23743 if(!vt[this.vtype](value, this)){
23747 if(typeof this.validator == "function"){
23748 var msg = this.validator(value);
23754 if(this.regex && !this.regex.test(value)){
23758 if(typeof(this.parseDate(value)) == 'undefined'){
23762 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23766 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23776 this.date = this.viewDate = '';
23778 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23783 Roo.apply(Roo.bootstrap.form.DateField, {
23794 html: '<i class="fa fa-arrow-left"/>'
23804 html: '<i class="fa fa-arrow-right"/>'
23846 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23847 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23848 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23849 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23850 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23863 navFnc: 'FullYear',
23868 navFnc: 'FullYear',
23873 Roo.apply(Roo.bootstrap.form.DateField, {
23877 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23881 cls: 'datepicker-days',
23885 cls: 'table-condensed',
23887 Roo.bootstrap.form.DateField.head,
23891 Roo.bootstrap.form.DateField.footer
23898 cls: 'datepicker-months',
23902 cls: 'table-condensed',
23904 Roo.bootstrap.form.DateField.head,
23905 Roo.bootstrap.form.DateField.content,
23906 Roo.bootstrap.form.DateField.footer
23913 cls: 'datepicker-years',
23917 cls: 'table-condensed',
23919 Roo.bootstrap.form.DateField.head,
23920 Roo.bootstrap.form.DateField.content,
23921 Roo.bootstrap.form.DateField.footer
23940 * @class Roo.bootstrap.form.TimeField
23941 * @extends Roo.bootstrap.form.Input
23942 * Bootstrap DateField class
23943 * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23947 * Create a new TimeField
23948 * @param {Object} config The config object
23951 Roo.bootstrap.form.TimeField = function(config){
23952 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23956 * Fires when this field show.
23957 * @param {Roo.bootstrap.form.DateField} thisthis
23958 * @param {Mixed} date The date value
23963 * Fires when this field hide.
23964 * @param {Roo.bootstrap.form.DateField} this
23965 * @param {Mixed} date The date value
23970 * Fires when select a date.
23971 * @param {Roo.bootstrap.form.DateField} this
23972 * @param {Mixed} date The date value
23978 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23981 * @cfg {String} format
23982 * The default time format string which can be overriden for localization support. The format must be
23983 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23988 getAutoCreate : function()
23990 this.after = '<i class="fa far fa-clock"></i>';
23991 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23995 onRender: function(ct, position)
23998 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24000 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24002 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24004 this.pop = this.picker().select('>.datepicker-time',true).first();
24005 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24007 this.picker().on('mousedown', this.onMousedown, this);
24008 this.picker().on('click', this.onClick, this);
24010 this.picker().addClass('datepicker-dropdown');
24015 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24016 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24017 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24018 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24019 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24020 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24024 fireKey: function(e){
24025 if (!this.picker().isVisible()){
24026 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24032 e.preventDefault();
24040 this.onTogglePeriod();
24043 this.onIncrementMinutes();
24046 this.onDecrementMinutes();
24055 onClick: function(e) {
24056 e.stopPropagation();
24057 e.preventDefault();
24060 picker : function()
24062 return this.pickerEl;
24065 fillTime: function()
24067 var time = this.pop.select('tbody', true).first();
24069 time.dom.innerHTML = '';
24084 cls: 'hours-up fa fas fa-chevron-up'
24104 cls: 'minutes-up fa fas fa-chevron-up'
24125 cls: 'timepicker-hour',
24140 cls: 'timepicker-minute',
24155 cls: 'btn btn-primary period',
24177 cls: 'hours-down fa fas fa-chevron-down'
24197 cls: 'minutes-down fa fas fa-chevron-down'
24214 // default minute is a multiple of minuteStep
24215 if(typeof(this.time) === 'undefined') {
24216 this.time = new Date();
24217 this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24219 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24226 var hours = this.time.getHours();
24227 var minutes = this.time.getMinutes();
24240 hours = hours - 12;
24244 hours = '0' + hours;
24248 minutes = '0' + minutes;
24251 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24252 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24253 this.pop.select('button', true).first().dom.innerHTML = period;
24259 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24261 var cls = ['bottom'];
24263 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24270 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24274 //this.picker().setXY(20000,20000);
24275 this.picker().addClass(cls.join('-'));
24279 Roo.each(cls, function(c){
24284 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24285 //_this.picker().setTop(_this.inputEl().getHeight());
24289 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24291 //_this.picker().setTop(0 - _this.picker().getHeight());
24296 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24300 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24308 onFocus : function()
24310 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24314 onBlur : function()
24316 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24322 this.picker().show();
24327 this.fireEvent('show', this, this.date);
24332 this.picker().hide();
24335 this.fireEvent('hide', this, this.date);
24338 setTime : function()
24341 this.setValue(this.time.format(this.format));
24343 this.fireEvent('select', this, this.date);
24348 onMousedown: function(e){
24349 e.stopPropagation();
24350 e.preventDefault();
24353 onIncrementHours: function()
24355 Roo.log('onIncrementHours');
24356 this.time = this.time.add(Date.HOUR, 1);
24361 onDecrementHours: function()
24363 Roo.log('onDecrementHours');
24364 this.time = this.time.add(Date.HOUR, -1);
24368 onIncrementMinutes: function()
24370 Roo.log('onIncrementMinutes');
24371 var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24372 this.time = this.time.add(Date.MINUTE, minutesToAdd);
24376 onDecrementMinutes: function()
24378 Roo.log('onDecrementMinutes');
24379 var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24380 this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24384 onTogglePeriod: function()
24386 Roo.log('onTogglePeriod');
24387 this.time = this.time.add(Date.HOUR, 12);
24395 Roo.apply(Roo.bootstrap.form.TimeField, {
24399 cls: 'datepicker dropdown-menu',
24403 cls: 'datepicker-time',
24407 cls: 'table-condensed',
24436 cls: 'btn btn-info ok',
24464 * @class Roo.bootstrap.form.MonthField
24465 * @extends Roo.bootstrap.form.Input
24466 * Bootstrap MonthField class
24468 * @cfg {String} language default en
24471 * Create a new MonthField
24472 * @param {Object} config The config object
24475 Roo.bootstrap.form.MonthField = function(config){
24476 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24481 * Fires when this field show.
24482 * @param {Roo.bootstrap.form.MonthField} this
24483 * @param {Mixed} date The date value
24488 * Fires when this field hide.
24489 * @param {Roo.bootstrap.form.MonthField} this
24490 * @param {Mixed} date The date value
24495 * Fires when select a date.
24496 * @param {Roo.bootstrap.form.MonthField} this
24497 * @param {String} oldvalue The old value
24498 * @param {String} newvalue The new value
24504 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24506 onRender: function(ct, position)
24509 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24511 this.language = this.language || 'en';
24512 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24513 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24515 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24516 this.isInline = false;
24517 this.isInput = true;
24518 this.component = this.el.select('.add-on', true).first() || false;
24519 this.component = (this.component && this.component.length === 0) ? false : this.component;
24520 this.hasInput = this.component && this.inputEL().length;
24522 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24524 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24526 this.picker().on('mousedown', this.onMousedown, this);
24527 this.picker().on('click', this.onClick, this);
24529 this.picker().addClass('datepicker-dropdown');
24531 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24532 v.setStyle('width', '189px');
24539 if(this.isInline) {
24545 setValue: function(v, suppressEvent)
24547 var o = this.getValue();
24549 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24553 if(suppressEvent !== true){
24554 this.fireEvent('select', this, o, v);
24559 getValue: function()
24564 onClick: function(e)
24566 e.stopPropagation();
24567 e.preventDefault();
24569 var target = e.getTarget();
24571 if(target.nodeName.toLowerCase() === 'i'){
24572 target = Roo.get(target).dom.parentNode;
24575 var nodeName = target.nodeName;
24576 var className = target.className;
24577 var html = target.innerHTML;
24579 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24583 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24585 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24591 picker : function()
24593 return this.pickerEl;
24596 fillMonths: function()
24599 var months = this.picker().select('>.datepicker-months td', true).first();
24601 months.dom.innerHTML = '';
24607 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24610 months.createChild(month);
24619 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24620 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24623 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24624 e.removeClass('active');
24626 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24627 e.addClass('active');
24634 if(this.isInline) {
24638 this.picker().removeClass(['bottom', 'top']);
24640 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24642 * place to the top of element!
24646 this.picker().addClass('top');
24647 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24652 this.picker().addClass('bottom');
24654 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24657 onFocus : function()
24659 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24663 onBlur : function()
24665 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24667 var d = this.inputEl().getValue();
24676 this.picker().show();
24677 this.picker().select('>.datepicker-months', true).first().show();
24681 this.fireEvent('show', this, this.date);
24686 if(this.isInline) {
24689 this.picker().hide();
24690 this.fireEvent('hide', this, this.date);
24694 onMousedown: function(e)
24696 e.stopPropagation();
24697 e.preventDefault();
24702 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24706 fireKey: function(e)
24708 if (!this.picker().isVisible()){
24709 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24720 e.preventDefault();
24724 dir = e.keyCode == 37 ? -1 : 1;
24726 this.vIndex = this.vIndex + dir;
24728 if(this.vIndex < 0){
24732 if(this.vIndex > 11){
24736 if(isNaN(this.vIndex)){
24740 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24746 dir = e.keyCode == 38 ? -1 : 1;
24748 this.vIndex = this.vIndex + dir * 4;
24750 if(this.vIndex < 0){
24754 if(this.vIndex > 11){
24758 if(isNaN(this.vIndex)){
24762 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24767 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24768 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24772 e.preventDefault();
24775 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24776 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24792 this.picker().remove();
24797 Roo.apply(Roo.bootstrap.form.MonthField, {
24816 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24817 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24822 Roo.apply(Roo.bootstrap.form.MonthField, {
24826 cls: 'datepicker dropdown-menu roo-dynamic',
24830 cls: 'datepicker-months',
24834 cls: 'table-condensed',
24836 Roo.bootstrap.form.DateField.content
24856 * @class Roo.bootstrap.form.CheckBox
24857 * @extends Roo.bootstrap.form.Input
24858 * Bootstrap CheckBox class
24860 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24861 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24862 * @cfg {String} boxLabel The text that appears beside the checkbox
24863 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24864 * @cfg {Boolean} checked initnal the element
24865 * @cfg {Boolean} inline inline the element (default false)
24866 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24867 * @cfg {String} tooltip label tooltip
24870 * Create a new CheckBox
24871 * @param {Object} config The config object
24874 Roo.bootstrap.form.CheckBox = function(config){
24875 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24880 * Fires when the element is checked or unchecked.
24881 * @param {Roo.bootstrap.form.CheckBox} this This input
24882 * @param {Boolean} checked The new checked value
24887 * Fires when the element is click.
24888 * @param {Roo.bootstrap.form.CheckBox} this This input
24895 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24897 inputType: 'checkbox',
24906 // checkbox success does not make any sense really..
24911 getAutoCreate : function()
24913 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24919 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24922 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24928 type : this.inputType,
24929 value : this.inputValue,
24930 cls : 'roo-' + this.inputType, //'form-box',
24931 placeholder : this.placeholder || ''
24935 if(this.inputType != 'radio'){
24939 cls : 'roo-hidden-value',
24940 value : this.checked ? this.inputValue : this.valueOff
24945 if (this.weight) { // Validity check?
24946 cfg.cls += " " + this.inputType + "-" + this.weight;
24949 if (this.disabled) {
24950 input.disabled=true;
24954 input.checked = this.checked;
24959 input.name = this.name;
24961 if(this.inputType != 'radio'){
24962 hidden.name = this.name;
24963 input.name = '_hidden_' + this.name;
24968 input.cls += ' input-' + this.size;
24973 ['xs','sm','md','lg'].map(function(size){
24974 if (settings[size]) {
24975 cfg.cls += ' col-' + size + '-' + settings[size];
24979 var inputblock = input;
24981 if (this.before || this.after) {
24984 cls : 'input-group',
24989 inputblock.cn.push({
24991 cls : 'input-group-addon',
24996 inputblock.cn.push(input);
24998 if(this.inputType != 'radio'){
24999 inputblock.cn.push(hidden);
25003 inputblock.cn.push({
25005 cls : 'input-group-addon',
25011 var boxLabelCfg = false;
25017 //'for': id, // box label is handled by onclick - so no for...
25019 html: this.boxLabel
25022 boxLabelCfg.tooltip = this.tooltip;
25028 if (align ==='left' && this.fieldLabel.length) {
25029 // Roo.log("left and has label");
25034 cls : 'control-label',
25035 html : this.fieldLabel
25046 cfg.cn[1].cn.push(boxLabelCfg);
25049 if(this.labelWidth > 12){
25050 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25053 if(this.labelWidth < 13 && this.labelmd == 0){
25054 this.labelmd = this.labelWidth;
25057 if(this.labellg > 0){
25058 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25059 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25062 if(this.labelmd > 0){
25063 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25064 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25067 if(this.labelsm > 0){
25068 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25069 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25072 if(this.labelxs > 0){
25073 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25074 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25077 } else if ( this.fieldLabel.length) {
25078 // Roo.log(" label");
25082 tag: this.boxLabel ? 'span' : 'label',
25084 cls: 'control-label box-input-label',
25085 //cls : 'input-group-addon',
25086 html : this.fieldLabel
25093 cfg.cn.push(boxLabelCfg);
25098 // Roo.log(" no label && no align");
25099 cfg.cn = [ inputblock ] ;
25101 cfg.cn.push(boxLabelCfg);
25109 if(this.inputType != 'radio'){
25110 cfg.cn.push(hidden);
25118 * return the real input element.
25120 inputEl: function ()
25122 return this.el.select('input.roo-' + this.inputType,true).first();
25124 hiddenEl: function ()
25126 return this.el.select('input.roo-hidden-value',true).first();
25129 labelEl: function()
25131 return this.el.select('label.control-label',true).first();
25133 /* depricated... */
25137 return this.labelEl();
25140 boxLabelEl: function()
25142 return this.el.select('label.box-label',true).first();
25145 initEvents : function()
25147 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25149 this.inputEl().on('click', this.onClick, this);
25151 if (this.boxLabel) {
25152 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25155 this.startValue = this.getValue();
25158 Roo.bootstrap.form.CheckBox.register(this);
25162 onClick : function(e)
25164 if(this.fireEvent('click', this, e) !== false){
25165 this.setChecked(!this.checked);
25170 setChecked : function(state,suppressEvent)
25172 this.startValue = this.getValue();
25174 if(this.inputType == 'radio'){
25176 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25177 e.dom.checked = false;
25180 this.inputEl().dom.checked = true;
25182 this.inputEl().dom.value = this.inputValue;
25184 if(suppressEvent !== true){
25185 this.fireEvent('check', this, true);
25193 this.checked = state;
25195 this.inputEl().dom.checked = state;
25198 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25200 if(suppressEvent !== true){
25201 this.fireEvent('check', this, state);
25207 getValue : function()
25209 if(this.inputType == 'radio'){
25210 return this.getGroupValue();
25213 return this.hiddenEl().dom.value;
25217 getGroupValue : function()
25219 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25223 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25226 setValue : function(v,suppressEvent)
25228 if(this.inputType == 'radio'){
25229 this.setGroupValue(v, suppressEvent);
25233 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25238 setGroupValue : function(v, suppressEvent)
25240 this.startValue = this.getValue();
25242 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25243 e.dom.checked = false;
25245 if(e.dom.value == v){
25246 e.dom.checked = true;
25250 if(suppressEvent !== true){
25251 this.fireEvent('check', this, true);
25259 validate : function()
25261 if(this.getVisibilityEl().hasClass('hidden')){
25267 (this.inputType == 'radio' && this.validateRadio()) ||
25268 (this.inputType == 'checkbox' && this.validateCheckbox())
25274 this.markInvalid();
25278 validateRadio : function()
25280 if(this.getVisibilityEl().hasClass('hidden')){
25284 if(this.allowBlank){
25290 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25291 if(!e.dom.checked){
25303 validateCheckbox : function()
25306 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25307 //return (this.getValue() == this.inputValue) ? true : false;
25310 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25318 for(var i in group){
25319 if(group[i].el.isVisible(true)){
25327 for(var i in group){
25332 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25339 * Mark this field as valid
25341 markValid : function()
25345 this.fireEvent('valid', this);
25347 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25350 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25357 if(this.inputType == 'radio'){
25358 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25359 var fg = e.findParent('.form-group', false, true);
25360 if (Roo.bootstrap.version == 3) {
25361 fg.removeClass([_this.invalidClass, _this.validClass]);
25362 fg.addClass(_this.validClass);
25364 fg.removeClass(['is-valid', 'is-invalid']);
25365 fg.addClass('is-valid');
25373 var fg = this.el.findParent('.form-group', false, true);
25374 if (Roo.bootstrap.version == 3) {
25375 fg.removeClass([this.invalidClass, this.validClass]);
25376 fg.addClass(this.validClass);
25378 fg.removeClass(['is-valid', 'is-invalid']);
25379 fg.addClass('is-valid');
25384 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25390 for(var i in group){
25391 var fg = group[i].el.findParent('.form-group', false, true);
25392 if (Roo.bootstrap.version == 3) {
25393 fg.removeClass([this.invalidClass, this.validClass]);
25394 fg.addClass(this.validClass);
25396 fg.removeClass(['is-valid', 'is-invalid']);
25397 fg.addClass('is-valid');
25403 * Mark this field as invalid
25404 * @param {String} msg The validation message
25406 markInvalid : function(msg)
25408 if(this.allowBlank){
25414 this.fireEvent('invalid', this, msg);
25416 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25419 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25423 label.markInvalid();
25426 if(this.inputType == 'radio'){
25428 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25429 var fg = e.findParent('.form-group', false, true);
25430 if (Roo.bootstrap.version == 3) {
25431 fg.removeClass([_this.invalidClass, _this.validClass]);
25432 fg.addClass(_this.invalidClass);
25434 fg.removeClass(['is-invalid', 'is-valid']);
25435 fg.addClass('is-invalid');
25443 var fg = this.el.findParent('.form-group', false, true);
25444 if (Roo.bootstrap.version == 3) {
25445 fg.removeClass([_this.invalidClass, _this.validClass]);
25446 fg.addClass(_this.invalidClass);
25448 fg.removeClass(['is-invalid', 'is-valid']);
25449 fg.addClass('is-invalid');
25454 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25460 for(var i in group){
25461 var fg = group[i].el.findParent('.form-group', false, true);
25462 if (Roo.bootstrap.version == 3) {
25463 fg.removeClass([_this.invalidClass, _this.validClass]);
25464 fg.addClass(_this.invalidClass);
25466 fg.removeClass(['is-invalid', 'is-valid']);
25467 fg.addClass('is-invalid');
25473 clearInvalid : function()
25475 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25477 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25479 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25481 if (label && label.iconEl) {
25482 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25483 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25487 disable : function()
25489 if(this.inputType != 'radio'){
25490 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25497 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25498 _this.getActionEl().addClass(this.disabledClass);
25499 e.dom.disabled = true;
25503 this.disabled = true;
25504 this.fireEvent("disable", this);
25508 enable : function()
25510 if(this.inputType != 'radio'){
25511 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25518 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25519 _this.getActionEl().removeClass(this.disabledClass);
25520 e.dom.disabled = false;
25524 this.disabled = false;
25525 this.fireEvent("enable", this);
25529 setBoxLabel : function(v)
25534 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25540 Roo.apply(Roo.bootstrap.form.CheckBox, {
25545 * register a CheckBox Group
25546 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25548 register : function(checkbox)
25550 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25551 this.groups[checkbox.groupId] = {};
25554 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25558 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25562 * fetch a CheckBox Group based on the group ID
25563 * @param {string} the group ID
25564 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25566 get: function(groupId) {
25567 if (typeof(this.groups[groupId]) == 'undefined') {
25571 return this.groups[groupId] ;
25584 * @class Roo.bootstrap.form.Radio
25585 * @extends Roo.bootstrap.Component
25586 * Bootstrap Radio class
25587 * @cfg {String} boxLabel - the label associated
25588 * @cfg {String} value - the value of radio
25591 * Create a new Radio
25592 * @param {Object} config The config object
25594 Roo.bootstrap.form.Radio = function(config){
25595 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25599 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25605 getAutoCreate : function()
25609 cls : 'form-group radio',
25614 html : this.boxLabel
25622 initEvents : function()
25624 this.parent().register(this);
25626 this.el.on('click', this.onClick, this);
25630 onClick : function(e)
25632 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25633 this.setChecked(true);
25637 setChecked : function(state, suppressEvent)
25639 this.parent().setValue(this.value, suppressEvent);
25643 setBoxLabel : function(v)
25648 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25663 * @class Roo.bootstrap.form.SecurePass
25664 * @extends Roo.bootstrap.form.Input
25665 * Bootstrap SecurePass class
25669 * Create a new SecurePass
25670 * @param {Object} config The config object
25673 Roo.bootstrap.form.SecurePass = function (config) {
25674 // these go here, so the translation tool can replace them..
25676 PwdEmpty: "Please type a password, and then retype it to confirm.",
25677 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25678 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25679 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25680 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25681 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25682 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25683 TooWeak: "Your password is Too Weak."
25685 this.meterLabel = "Password strength:";
25686 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25687 this.meterClass = [
25688 "roo-password-meter-tooweak",
25689 "roo-password-meter-weak",
25690 "roo-password-meter-medium",
25691 "roo-password-meter-strong",
25692 "roo-password-meter-grey"
25697 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25700 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25702 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25704 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25705 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25706 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25707 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25708 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25709 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25710 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25720 * @cfg {String/Object} Label for the strength meter (defaults to
25721 * 'Password strength:')
25726 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25727 * ['Weak', 'Medium', 'Strong'])
25730 pwdStrengths: false,
25743 initEvents: function ()
25745 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25747 if (this.el.is('input[type=password]') && Roo.isSafari) {
25748 this.el.on('keydown', this.SafariOnKeyDown, this);
25751 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25754 onRender: function (ct, position)
25756 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25757 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25758 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25760 this.trigger.createChild({
25765 cls: 'roo-password-meter-grey col-xs-12',
25768 //width: this.meterWidth + 'px'
25772 cls: 'roo-password-meter-text'
25778 if (this.hideTrigger) {
25779 this.trigger.setDisplayed(false);
25781 this.setSize(this.width || '', this.height || '');
25784 onDestroy: function ()
25786 if (this.trigger) {
25787 this.trigger.removeAllListeners();
25788 this.trigger.remove();
25791 this.wrap.remove();
25793 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25796 checkStrength: function ()
25798 var pwd = this.inputEl().getValue();
25799 if (pwd == this._lastPwd) {
25804 if (this.ClientSideStrongPassword(pwd)) {
25806 } else if (this.ClientSideMediumPassword(pwd)) {
25808 } else if (this.ClientSideWeakPassword(pwd)) {
25814 Roo.log('strength1: ' + strength);
25816 //var pm = this.trigger.child('div/div/div').dom;
25817 var pm = this.trigger.child('div/div');
25818 pm.removeClass(this.meterClass);
25819 pm.addClass(this.meterClass[strength]);
25822 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25824 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25826 this._lastPwd = pwd;
25830 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25832 this._lastPwd = '';
25834 var pm = this.trigger.child('div/div');
25835 pm.removeClass(this.meterClass);
25836 pm.addClass('roo-password-meter-grey');
25839 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25842 this.inputEl().dom.type='password';
25845 validateValue: function (value)
25847 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25850 if (value.length == 0) {
25851 if (this.allowBlank) {
25852 this.clearInvalid();
25856 this.markInvalid(this.errors.PwdEmpty);
25857 this.errorMsg = this.errors.PwdEmpty;
25865 if (!value.match(/[\x21-\x7e]+/)) {
25866 this.markInvalid(this.errors.PwdBadChar);
25867 this.errorMsg = this.errors.PwdBadChar;
25870 if (value.length < 6) {
25871 this.markInvalid(this.errors.PwdShort);
25872 this.errorMsg = this.errors.PwdShort;
25875 if (value.length > 16) {
25876 this.markInvalid(this.errors.PwdLong);
25877 this.errorMsg = this.errors.PwdLong;
25881 if (this.ClientSideStrongPassword(value)) {
25883 } else if (this.ClientSideMediumPassword(value)) {
25885 } else if (this.ClientSideWeakPassword(value)) {
25892 if (strength < 2) {
25893 //this.markInvalid(this.errors.TooWeak);
25894 this.errorMsg = this.errors.TooWeak;
25899 console.log('strength2: ' + strength);
25901 //var pm = this.trigger.child('div/div/div').dom;
25903 var pm = this.trigger.child('div/div');
25904 pm.removeClass(this.meterClass);
25905 pm.addClass(this.meterClass[strength]);
25907 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25909 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25911 this.errorMsg = '';
25915 CharacterSetChecks: function (type)
25918 this.fResult = false;
25921 isctype: function (character, type)
25924 case this.kCapitalLetter:
25925 if (character >= 'A' && character <= 'Z') {
25930 case this.kSmallLetter:
25931 if (character >= 'a' && character <= 'z') {
25937 if (character >= '0' && character <= '9') {
25942 case this.kPunctuation:
25943 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25954 IsLongEnough: function (pwd, size)
25956 return !(pwd == null || isNaN(size) || pwd.length < size);
25959 SpansEnoughCharacterSets: function (word, nb)
25961 if (!this.IsLongEnough(word, nb))
25966 var characterSetChecks = new Array(
25967 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25968 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25971 for (var index = 0; index < word.length; ++index) {
25972 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25973 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25974 characterSetChecks[nCharSet].fResult = true;
25981 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25982 if (characterSetChecks[nCharSet].fResult) {
25987 if (nCharSets < nb) {
25993 ClientSideStrongPassword: function (pwd)
25995 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25998 ClientSideMediumPassword: function (pwd)
26000 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26003 ClientSideWeakPassword: function (pwd)
26005 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26008 });Roo.rtf = {}; // namespace
26009 Roo.rtf.Hex = function(hex)
26013 Roo.rtf.Paragraph = function(opts)
26015 this.content = []; ///??? is that used?
26016 };Roo.rtf.Span = function(opts)
26018 this.value = opts.value;
26021 Roo.rtf.Group = function(parent)
26023 // we dont want to acutally store parent - it will make debug a nightmare..
26031 Roo.rtf.Group.prototype = {
26035 addContent : function(node) {
26036 // could set styles...
26037 this.content.push(node);
26039 addChild : function(cn)
26043 // only for images really...
26044 toDataURL : function()
26046 var mimetype = false;
26048 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
26049 mimetype = "image/png";
26051 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26052 mimetype = "image/jpeg";
26055 return 'about:blank'; // ?? error?
26059 var hexstring = this.content[this.content.length-1].value;
26061 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26062 return String.fromCharCode(parseInt(a, 16));
26067 // this looks like it's normally the {rtf{ .... }}
26068 Roo.rtf.Document = function()
26070 // we dont want to acutally store parent - it will make debug a nightmare..
26076 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
26077 addChild : function(cn)
26081 case 'rtlch': // most content seems to be inside this??
26084 this.rtlch.push(cn);
26087 this[cn.type] = cn;
26092 getElementsByType : function(type)
26095 this._getElementsByType(type, ret, this.cn, 'rtf');
26098 _getElementsByType : function (type, ret, search_array, path)
26100 search_array.forEach(function(n,i) {
26101 if (n.type == type) {
26102 n.path = path + '/' + n.type + ':' + i;
26105 if (n.cn.length > 0) {
26106 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26113 Roo.rtf.Ctrl = function(opts)
26115 this.value = opts.value;
26116 this.param = opts.param;
26121 * based on this https://github.com/iarna/rtf-parser
26122 * it's really only designed to extract pict from pasted RTF
26126 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26135 Roo.rtf.Parser = function(text) {
26136 //super({objectMode: true})
26138 this.parserState = this.parseText;
26140 // these are for interpeter...
26142 ///this.parserState = this.parseTop
26143 this.groupStack = [];
26144 this.hexStore = [];
26147 this.groups = []; // where we put the return.
26149 for (var ii = 0; ii < text.length; ++ii) {
26152 if (text[ii] === '\n') {
26158 this.parserState(text[ii]);
26164 Roo.rtf.Parser.prototype = {
26165 text : '', // string being parsed..
26167 controlWordParam : '',
26171 groupStack : false,
26176 row : 1, // reportin?
26180 push : function (el)
26182 var m = 'cmd'+ el.type;
26183 if (typeof(this[m]) == 'undefined') {
26184 Roo.log('invalid cmd:' + el.type);
26190 flushHexStore : function()
26192 if (this.hexStore.length < 1) {
26195 var hexstr = this.hexStore.map(
26200 this.group.addContent( new Roo.rtf.Hex( hexstr ));
26203 this.hexStore.splice(0)
26207 cmdgroupstart : function()
26209 this.flushHexStore();
26211 this.groupStack.push(this.group);
26214 if (this.doc === false) {
26215 this.group = this.doc = new Roo.rtf.Document();
26219 this.group = new Roo.rtf.Group(this.group);
26221 cmdignorable : function()
26223 this.flushHexStore();
26224 this.group.ignorable = true;
26226 cmdendparagraph : function()
26228 this.flushHexStore();
26229 this.group.addContent(new Roo.rtf.Paragraph());
26231 cmdgroupend : function ()
26233 this.flushHexStore();
26234 var endingGroup = this.group;
26237 this.group = this.groupStack.pop();
26239 this.group.addChild(endingGroup);
26244 var doc = this.group || this.doc;
26245 //if (endingGroup instanceof FontTable) {
26246 // doc.fonts = endingGroup.table
26247 //} else if (endingGroup instanceof ColorTable) {
26248 // doc.colors = endingGroup.table
26249 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26250 if (endingGroup.ignorable === false) {
26252 this.groups.push(endingGroup);
26253 // Roo.log( endingGroup );
26255 //Roo.each(endingGroup.content, function(item)) {
26256 // doc.addContent(item);
26258 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26261 cmdtext : function (cmd)
26263 this.flushHexStore();
26264 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26265 //this.group = this.doc
26266 return; // we really don't care about stray text...
26268 this.group.addContent(new Roo.rtf.Span(cmd));
26270 cmdcontrolword : function (cmd)
26272 this.flushHexStore();
26273 if (!this.group.type) {
26274 this.group.type = cmd.value;
26277 this.group.addContent(new Roo.rtf.Ctrl(cmd));
26278 // we actually don't care about ctrl words...
26281 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26282 if (this[method]) {
26283 this[method](cmd.param)
26285 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26289 cmdhexchar : function(cmd) {
26290 this.hexStore.push(cmd);
26292 cmderror : function(cmd) {
26298 if (this.text !== '\u0000') this.emitText()
26304 parseText : function(c)
26307 this.parserState = this.parseEscapes;
26308 } else if (c === '{') {
26309 this.emitStartGroup();
26310 } else if (c === '}') {
26311 this.emitEndGroup();
26312 } else if (c === '\x0A' || c === '\x0D') {
26313 // cr/lf are noise chars
26319 parseEscapes: function (c)
26321 if (c === '\\' || c === '{' || c === '}') {
26323 this.parserState = this.parseText;
26325 this.parserState = this.parseControlSymbol;
26326 this.parseControlSymbol(c);
26329 parseControlSymbol: function(c)
26332 this.text += '\u00a0'; // nbsp
26333 this.parserState = this.parseText
26334 } else if (c === '-') {
26335 this.text += '\u00ad'; // soft hyphen
26336 } else if (c === '_') {
26337 this.text += '\u2011'; // non-breaking hyphen
26338 } else if (c === '*') {
26339 this.emitIgnorable();
26340 this.parserState = this.parseText;
26341 } else if (c === "'") {
26342 this.parserState = this.parseHexChar;
26343 } else if (c === '|') { // formula cacter
26344 this.emitFormula();
26345 this.parserState = this.parseText;
26346 } else if (c === ':') { // subentry in an index entry
26347 this.emitIndexSubEntry();
26348 this.parserState = this.parseText;
26349 } else if (c === '\x0a') {
26350 this.emitEndParagraph();
26351 this.parserState = this.parseText;
26352 } else if (c === '\x0d') {
26353 this.emitEndParagraph();
26354 this.parserState = this.parseText;
26356 this.parserState = this.parseControlWord;
26357 this.parseControlWord(c);
26360 parseHexChar: function (c)
26362 if (/^[A-Fa-f0-9]$/.test(c)) {
26364 if (this.hexChar.length >= 2) {
26365 this.emitHexChar();
26366 this.parserState = this.parseText;
26370 this.emitError("Invalid character \"" + c + "\" in hex literal.");
26371 this.parserState = this.parseText;
26374 parseControlWord : function(c)
26377 this.emitControlWord();
26378 this.parserState = this.parseText;
26379 } else if (/^[-\d]$/.test(c)) {
26380 this.parserState = this.parseControlWordParam;
26381 this.controlWordParam += c;
26382 } else if (/^[A-Za-z]$/.test(c)) {
26383 this.controlWord += c;
26385 this.emitControlWord();
26386 this.parserState = this.parseText;
26390 parseControlWordParam : function (c) {
26391 if (/^\d$/.test(c)) {
26392 this.controlWordParam += c;
26393 } else if (c === ' ') {
26394 this.emitControlWord();
26395 this.parserState = this.parseText;
26397 this.emitControlWord();
26398 this.parserState = this.parseText;
26406 emitText : function () {
26407 if (this.text === '') {
26419 emitControlWord : function ()
26422 if (this.controlWord === '') {
26423 // do we want to track this - it seems just to cause problems.
26424 //this.emitError('empty control word');
26427 type: 'controlword',
26428 value: this.controlWord,
26429 param: this.controlWordParam !== '' && Number(this.controlWordParam),
26435 this.controlWord = '';
26436 this.controlWordParam = '';
26438 emitStartGroup : function ()
26442 type: 'groupstart',
26448 emitEndGroup : function ()
26458 emitIgnorable : function ()
26468 emitHexChar : function ()
26473 value: this.hexChar,
26480 emitError : function (message)
26488 char: this.cpos //,
26489 //stack: new Error().stack
26492 emitEndParagraph : function () {
26495 type: 'endparagraph',
26504 * @class Roo.htmleditor.Filter
26505 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26506 * @cfg {DomElement} node The node to iterate and filter
26507 * @cfg {boolean|String|Array} tag Tags to replace
26509 * Create a new Filter.
26510 * @param {Object} config Configuration options
26515 Roo.htmleditor.Filter = function(cfg) {
26516 Roo.apply(this.cfg);
26517 // this does not actually call walk as it's really just a abstract class
26521 Roo.htmleditor.Filter.prototype = {
26527 // overrride to do replace comments.
26528 replaceComment : false,
26530 // overrride to do replace or do stuff with tags..
26531 replaceTag : false,
26533 walk : function(dom)
26535 Roo.each( Array.from(dom.childNodes), function( e ) {
26538 case e.nodeType == 8 && this.replaceComment !== false: // comment
26539 this.replaceComment(e);
26542 case e.nodeType != 1: //not a node.
26545 case this.tag === true: // everything
26546 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26547 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26548 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26549 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26550 if (this.replaceTag && false === this.replaceTag(e)) {
26553 if (e.hasChildNodes()) {
26558 default: // tags .. that do not match.
26559 if (e.hasChildNodes()) {
26569 removeNodeKeepChildren : function( node)
26572 ar = Array.from(node.childNodes);
26573 for (var i = 0; i < ar.length; i++) {
26575 node.removeChild(ar[i]);
26576 // what if we need to walk these???
26577 node.parentNode.insertBefore(ar[i], node);
26580 node.parentNode.removeChild(node);
26585 * @class Roo.htmleditor.FilterAttributes
26586 * clean attributes and styles including http:// etc.. in attribute
26588 * Run a new Attribute Filter
26589 * @param {Object} config Configuration options
26591 Roo.htmleditor.FilterAttributes = function(cfg)
26593 Roo.apply(this, cfg);
26594 this.attrib_black = this.attrib_black || [];
26595 this.attrib_white = this.attrib_white || [];
26597 this.attrib_clean = this.attrib_clean || [];
26598 this.style_white = this.style_white || [];
26599 this.style_black = this.style_black || [];
26600 this.walk(cfg.node);
26603 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26605 tag: true, // all tags
26607 attrib_black : false, // array
26608 attrib_clean : false,
26609 attrib_white : false,
26611 style_white : false,
26612 style_black : false,
26615 replaceTag : function(node)
26617 if (!node.attributes || !node.attributes.length) {
26621 for (var i = node.attributes.length-1; i > -1 ; i--) {
26622 var a = node.attributes[i];
26624 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26625 node.removeAttribute(a.name);
26631 if (a.name.toLowerCase().substr(0,2)=='on') {
26632 node.removeAttribute(a.name);
26637 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26638 node.removeAttribute(a.name);
26641 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26642 this.cleanAttr(node,a.name,a.value); // fixme..
26645 if (a.name == 'style') {
26646 this.cleanStyle(node,a.name,a.value);
26649 /// clean up MS crap..
26650 // tecnically this should be a list of valid class'es..
26653 if (a.name == 'class') {
26654 if (a.value.match(/^Mso/)) {
26655 node.removeAttribute('class');
26658 if (a.value.match(/^body$/)) {
26659 node.removeAttribute('class');
26669 return true; // clean children
26672 cleanAttr: function(node, n,v)
26675 if (v.match(/^\./) || v.match(/^\//)) {
26678 if (v.match(/^(http|https):\/\//)
26679 || v.match(/^mailto:/)
26680 || v.match(/^ftp:/)
26681 || v.match(/^data:/)
26685 if (v.match(/^#/)) {
26688 if (v.match(/^\{/)) { // allow template editing.
26691 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26692 node.removeAttribute(n);
26695 cleanStyle : function(node, n,v)
26697 if (v.match(/expression/)) { //XSS?? should we even bother..
26698 node.removeAttribute(n);
26702 var parts = v.split(/;/);
26705 Roo.each(parts, function(p) {
26706 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26710 var l = p.split(':').shift().replace(/\s+/g,'');
26711 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26713 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26717 // only allow 'c whitelisted system attributes'
26718 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26726 if (clean.length) {
26727 node.setAttribute(n, clean.join(';'));
26729 node.removeAttribute(n);
26738 * @class Roo.htmleditor.FilterBlack
26739 * remove blacklisted elements.
26741 * Run a new Blacklisted Filter
26742 * @param {Object} config Configuration options
26745 Roo.htmleditor.FilterBlack = function(cfg)
26747 Roo.apply(this, cfg);
26748 this.walk(cfg.node);
26751 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26753 tag : true, // all elements.
26755 replaceTag : function(n)
26757 n.parentNode.removeChild(n);
26761 * @class Roo.htmleditor.FilterComment
26764 * Run a new Comments Filter
26765 * @param {Object} config Configuration options
26767 Roo.htmleditor.FilterComment = function(cfg)
26769 this.walk(cfg.node);
26772 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26775 replaceComment : function(n)
26777 n.parentNode.removeChild(n);
26780 * @class Roo.htmleditor.FilterKeepChildren
26781 * remove tags but keep children
26783 * Run a new Keep Children Filter
26784 * @param {Object} config Configuration options
26787 Roo.htmleditor.FilterKeepChildren = function(cfg)
26789 Roo.apply(this, cfg);
26790 if (this.tag === false) {
26791 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26794 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26795 this.cleanNamespace = true;
26798 this.walk(cfg.node);
26801 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26803 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26805 replaceTag : function(node)
26807 // walk children...
26808 //Roo.log(node.tagName);
26809 var ar = Array.from(node.childNodes);
26812 for (var i = 0; i < ar.length; i++) {
26814 if (e.nodeType == 1) {
26816 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26817 || // array and it matches
26818 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26820 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26822 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26824 this.replaceTag(ar[i]); // child is blacklisted as well...
26829 ar = Array.from(node.childNodes);
26830 for (var i = 0; i < ar.length; i++) {
26832 node.removeChild(ar[i]);
26833 // what if we need to walk these???
26834 node.parentNode.insertBefore(ar[i], node);
26835 if (this.tag !== false) {
26840 //Roo.log("REMOVE:" + node.tagName);
26841 node.parentNode.removeChild(node);
26842 return false; // don't walk children
26847 * @class Roo.htmleditor.FilterParagraph
26848 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26849 * like on 'push' to remove the <p> tags and replace them with line breaks.
26851 * Run a new Paragraph Filter
26852 * @param {Object} config Configuration options
26855 Roo.htmleditor.FilterParagraph = function(cfg)
26857 // no need to apply config.
26858 this.walk(cfg.node);
26861 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26868 replaceTag : function(node)
26871 if (node.childNodes.length == 1 &&
26872 node.childNodes[0].nodeType == 3 &&
26873 node.childNodes[0].textContent.trim().length < 1
26875 // remove and replace with '<BR>';
26876 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26877 return false; // no need to walk..
26879 var ar = Array.from(node.childNodes);
26880 for (var i = 0; i < ar.length; i++) {
26881 node.removeChild(ar[i]);
26882 // what if we need to walk these???
26883 node.parentNode.insertBefore(ar[i], node);
26885 // now what about this?
26889 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26890 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26891 node.parentNode.removeChild(node);
26898 * @class Roo.htmleditor.FilterSpan
26899 * filter span's with no attributes out..
26901 * Run a new Span Filter
26902 * @param {Object} config Configuration options
26905 Roo.htmleditor.FilterSpan = function(cfg)
26907 // no need to apply config.
26908 this.walk(cfg.node);
26911 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26917 replaceTag : function(node)
26919 if (node.attributes && node.attributes.length > 0) {
26920 return true; // walk if there are any.
26922 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26928 * @class Roo.htmleditor.FilterTableWidth
26929 try and remove table width data - as that frequently messes up other stuff.
26931 * was cleanTableWidths.
26933 * Quite often pasting from word etc.. results in tables with column and widths.
26934 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26937 * Run a new Table Filter
26938 * @param {Object} config Configuration options
26941 Roo.htmleditor.FilterTableWidth = function(cfg)
26943 // no need to apply config.
26944 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26945 this.walk(cfg.node);
26948 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26953 replaceTag: function(node) {
26957 if (node.hasAttribute('width')) {
26958 node.removeAttribute('width');
26962 if (node.hasAttribute("style")) {
26965 var styles = node.getAttribute("style").split(";");
26967 Roo.each(styles, function(s) {
26968 if (!s.match(/:/)) {
26971 var kv = s.split(":");
26972 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26975 // what ever is left... we allow.
26978 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26979 if (!nstyle.length) {
26980 node.removeAttribute('style');
26984 return true; // continue doing children..
26987 * @class Roo.htmleditor.FilterWord
26988 * try and clean up all the mess that Word generates.
26990 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26993 * Run a new Span Filter
26994 * @param {Object} config Configuration options
26997 Roo.htmleditor.FilterWord = function(cfg)
26999 // no need to apply config.
27000 this.replaceDocBullets(cfg.node);
27002 this.replaceAname(cfg.node);
27003 // this is disabled as the removal is done by other filters;
27004 // this.walk(cfg.node);
27005 this.replaceImageTable(cfg.node);
27009 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27015 * Clean up MS wordisms...
27017 replaceTag : function(node)
27020 // no idea what this does - span with text, replaceds with just text.
27022 node.nodeName == 'SPAN' &&
27023 !node.hasAttributes() &&
27024 node.childNodes.length == 1 &&
27025 node.firstChild.nodeName == "#text"
27027 var textNode = node.firstChild;
27028 node.removeChild(textNode);
27029 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27030 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27032 node.parentNode.insertBefore(textNode, node);
27033 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27034 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27037 node.parentNode.removeChild(node);
27038 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27043 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27044 node.parentNode.removeChild(node);
27045 return false; // dont do chidlren
27047 //Roo.log(node.tagName);
27048 // remove - but keep children..
27049 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27050 //Roo.log('-- removed');
27051 while (node.childNodes.length) {
27052 var cn = node.childNodes[0];
27053 node.removeChild(cn);
27054 node.parentNode.insertBefore(cn, node);
27055 // move node to parent - and clean it..
27056 if (cn.nodeType == 1) {
27057 this.replaceTag(cn);
27061 node.parentNode.removeChild(node);
27062 /// no need to iterate chidlren = it's got none..
27063 //this.iterateChildren(node, this.cleanWord);
27064 return false; // no need to iterate children.
27067 if (node.className.length) {
27069 var cn = node.className.split(/\W+/);
27071 Roo.each(cn, function(cls) {
27072 if (cls.match(/Mso[a-zA-Z]+/)) {
27077 node.className = cna.length ? cna.join(' ') : '';
27079 node.removeAttribute("class");
27083 if (node.hasAttribute("lang")) {
27084 node.removeAttribute("lang");
27087 if (node.hasAttribute("style")) {
27089 var styles = node.getAttribute("style").split(";");
27091 Roo.each(styles, function(s) {
27092 if (!s.match(/:/)) {
27095 var kv = s.split(":");
27096 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27099 // what ever is left... we allow.
27102 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27103 if (!nstyle.length) {
27104 node.removeAttribute('style');
27107 return true; // do children
27113 styleToObject: function(node)
27115 var styles = (node.getAttribute("style") || '').split(";");
27117 Roo.each(styles, function(s) {
27118 if (!s.match(/:/)) {
27121 var kv = s.split(":");
27123 // what ever is left... we allow.
27124 ret[kv[0].trim()] = kv[1];
27130 replaceAname : function (doc)
27132 // replace all the a/name without..
27133 var aa = Array.from(doc.getElementsByTagName('a'));
27134 for (var i = 0; i < aa.length; i++) {
27136 if (a.hasAttribute("name")) {
27137 a.removeAttribute("name");
27139 if (a.hasAttribute("href")) {
27142 // reparent children.
27143 this.removeNodeKeepChildren(a);
27153 replaceDocBullets : function(doc)
27155 // this is a bit odd - but it appears some indents use ql-indent-1
27156 //Roo.log(doc.innerHTML);
27158 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27159 for( var i = 0; i < listpara.length; i ++) {
27160 listpara[i].className = "MsoListParagraph";
27163 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27164 for( var i = 0; i < listpara.length; i ++) {
27165 listpara[i].className = "MsoListParagraph";
27167 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27168 for( var i = 0; i < listpara.length; i ++) {
27169 listpara[i].className = "MsoListParagraph";
27171 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
27172 for( var i = 0; i < listpara.length; i ++) {
27173 listpara[i].className = "MsoListParagraph";
27176 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27177 var htwo = Array.from(doc.getElementsByTagName('h2'));
27178 for( var i = 0; i < htwo.length; i ++) {
27179 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27180 htwo[i].className = "MsoListParagraph";
27183 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
27184 for( var i = 0; i < listpara.length; i ++) {
27185 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27186 listpara[i].className = "MsoListParagraph";
27188 listpara[i].className = "MsoNormalx";
27192 listpara = doc.getElementsByClassName('MsoListParagraph');
27193 // Roo.log(doc.innerHTML);
27197 while(listpara.length) {
27199 this.replaceDocBullet(listpara.item(0));
27206 replaceDocBullet : function(p)
27208 // gather all the siblings.
27210 parent = p.parentNode,
27211 doc = parent.ownerDocument,
27214 //Roo.log("Parsing: " + p.innerText) ;
27215 var listtype = 'ul';
27217 if (ns.nodeType != 1) {
27218 ns = ns.nextSibling;
27221 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27222 //Roo.log("Missing para r q1indent - got:" + ns.className);
27225 var spans = ns.getElementsByTagName('span');
27227 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27229 ns = ns.nextSibling;
27231 if (!spans.length) {
27236 for (var i = 0; i < spans.length;i++) {
27238 if (se.hasAttribute('style') && se.hasAttribute('style') && se.style.fontFamily != '') {
27239 ff = se.style.fontFamily;
27245 //Roo.log("got font family: " + ff);
27246 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27252 //Roo.log("no mso-list?");
27254 var spans = ns.getElementsByTagName('span');
27255 if (!spans.length) {
27258 var has_list = false;
27259 for(var i = 0; i < spans.length; i++) {
27260 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27269 ns = ns.nextSibling;
27273 if (!items.length) {
27278 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27279 parent.insertBefore(ul, p);
27281 var stack = [ ul ];
27282 var last_li = false;
27284 var margin_to_depth = {};
27287 items.forEach(function(n, ipos) {
27288 //Roo.log("got innertHMLT=" + n.innerHTML);
27290 var spans = n.getElementsByTagName('span');
27291 if (!spans.length) {
27292 //Roo.log("No spans found");
27294 parent.removeChild(n);
27297 return; // skip it...
27303 for(var i = 0; i < spans.length; i++) {
27305 style = this.styleToObject(spans[i]);
27306 if (typeof(style['mso-list']) == 'undefined') {
27309 if (listtype == 'ol') {
27310 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
27312 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27315 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27316 style = this.styleToObject(n); // mo-list is from the parent node.
27317 if (typeof(style['mso-list']) == 'undefined') {
27318 //Roo.log("parent is missing level");
27320 parent.removeChild(n);
27325 var margin = style['margin-left'];
27326 if (typeof(margin_to_depth[margin]) == 'undefined') {
27328 margin_to_depth[margin] = max_margins;
27330 nlvl = margin_to_depth[margin] ;
27334 var nul = doc.createElement(listtype); // what about number lists...
27336 last_li = doc.createElement('li');
27337 stack[lvl].appendChild(last_li);
27339 last_li.appendChild(nul);
27345 // not starting at 1..
27346 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27347 stack[nlvl].setAttribute("start", num);
27350 var nli = stack[nlvl].appendChild(doc.createElement('li'));
27352 nli.innerHTML = n.innerHTML;
27353 //Roo.log("innerHTML = " + n.innerHTML);
27354 parent.removeChild(n);
27366 replaceImageTable : function(doc)
27369 <table cellpadding=0 cellspacing=0 align=left>
27371 <td width=423 height=0></td>
27375 <td><img width=601 height=401
27376 src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27377 v:shapes="Picture_x0020_2"></td>
27381 var imgs = Array.from(doc.getElementsByTagName('img'));
27382 Roo.each(imgs, function(img) {
27383 var td = img.parentNode;
27384 if (td.nodeName != 'TD') {
27387 var tr = td.parentNode;
27388 if (tr.nodeName != 'TR') {
27391 var tbody = tr.parentNode;
27392 if (tbody.nodeName != 'TBODY') {
27395 var table = tbody.parentNode;
27396 if (table.nodeName != 'TABLE') {
27401 if (table.getElementsByTagName('tr').length != 2) {
27404 if (table.getElementsByTagName('td').length != 3) {
27407 if (table.innerText.trim() != '') {
27410 var p = table.parentNode;
27411 img.parentNode.removeChild(img);
27412 p.insertBefore(img, table);
27413 p.removeChild(table);
27424 * @class Roo.htmleditor.FilterStyleToTag
27425 * part of the word stuff... - certain 'styles' should be converted to tags.
27427 * font-weight: bold -> bold
27428 * ?? super / subscrit etc..
27431 * Run a new style to tag filter.
27432 * @param {Object} config Configuration options
27434 Roo.htmleditor.FilterStyleToTag = function(cfg)
27438 B : [ 'fontWeight' , 'bold'],
27439 I : [ 'fontStyle' , 'italic'],
27440 //pre : [ 'font-style' , 'italic'],
27441 // h1.. h6 ?? font-size?
27442 SUP : [ 'verticalAlign' , 'super' ],
27443 SUB : [ 'verticalAlign' , 'sub' ]
27448 Roo.apply(this, cfg);
27451 this.walk(cfg.node);
27458 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27460 tag: true, // all tags
27465 replaceTag : function(node)
27469 if (node.getAttribute("style") === null) {
27473 for (var k in this.tags) {
27474 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27476 node.style.removeProperty(this.tags[k][0]);
27479 if (!inject.length) {
27482 var cn = Array.from(node.childNodes);
27484 Roo.each(inject, function(t) {
27485 var nc = node.ownerDocument.createElement(t);
27486 nn.appendChild(nc);
27489 for(var i = 0;i < cn.length;cn++) {
27490 node.removeChild(cn[i]);
27491 nn.appendChild(cn[i]);
27493 return true /// iterate thru
27497 * @class Roo.htmleditor.FilterLongBr
27498 * BR/BR/BR - keep a maximum of 2...
27500 * Run a new Long BR Filter
27501 * @param {Object} config Configuration options
27504 Roo.htmleditor.FilterLongBr = function(cfg)
27506 // no need to apply config.
27507 this.walk(cfg.node);
27510 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27517 replaceTag : function(node)
27520 var ps = node.nextSibling;
27521 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27522 ps = ps.nextSibling;
27525 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
27526 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27530 if (!ps || ps.nodeType != 1) {
27534 if (!ps || ps.tagName != 'BR') {
27543 if (!node.previousSibling) {
27546 var ps = node.previousSibling;
27548 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27549 ps = ps.previousSibling;
27551 if (!ps || ps.nodeType != 1) {
27554 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27555 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27559 node.parentNode.removeChild(node); // remove me...
27561 return false; // no need to do children
27568 * @class Roo.htmleditor.FilterBlock
27569 * removes id / data-block and contenteditable that are associated with blocks
27570 * usage should be done on a cloned copy of the dom
27572 * Run a new Attribute Filter { node : xxxx }}
27573 * @param {Object} config Configuration options
27575 Roo.htmleditor.FilterBlock = function(cfg)
27577 Roo.apply(this, cfg);
27578 var qa = cfg.node.querySelectorAll;
27579 this.removeAttributes('data-block');
27580 this.removeAttributes('contenteditable');
27581 this.removeAttributes('id');
27585 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27587 node: true, // all tags
27590 removeAttributes : function(attr)
27592 var ar = this.node.querySelectorAll('*[' + attr + ']');
27593 for (var i =0;i<ar.length;i++) {
27594 ar[i].removeAttribute(attr);
27603 * This is based loosely on tinymce
27604 * @class Roo.htmleditor.TidySerializer
27605 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27607 * @method Serializer
27608 * @param {Object} settings Name/value settings object.
27612 Roo.htmleditor.TidySerializer = function(settings)
27614 Roo.apply(this, settings);
27616 this.writer = new Roo.htmleditor.TidyWriter(settings);
27621 Roo.htmleditor.TidySerializer.prototype = {
27624 * @param {boolean} inner do the inner of the node.
27631 * Serializes the specified node into a string.
27634 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27635 * @method serialize
27636 * @param {DomElement} node Node instance to serialize.
27637 * @return {String} String with HTML based on DOM tree.
27639 serialize : function(node) {
27641 // = settings.validate;
27642 var writer = this.writer;
27646 3: function(node) {
27648 writer.text(node.nodeValue, node);
27651 8: function(node) {
27652 writer.comment(node.nodeValue);
27654 // Processing instruction
27655 7: function(node) {
27656 writer.pi(node.name, node.nodeValue);
27659 10: function(node) {
27660 writer.doctype(node.nodeValue);
27663 4: function(node) {
27664 writer.cdata(node.nodeValue);
27666 // Document fragment
27667 11: function(node) {
27668 node = node.firstChild;
27674 node = node.nextSibling
27679 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27680 return writer.getContent();
27683 walk: function(node)
27685 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27686 handler = this.handlers[node.nodeType];
27693 var name = node.nodeName;
27694 var isEmpty = node.childNodes.length < 1;
27696 var writer = this.writer;
27697 var attrs = node.attributes;
27700 writer.start(node.nodeName, attrs, isEmpty, node);
27704 node = node.firstChild;
27711 node = node.nextSibling;
27717 // Serialize element and treat all non elements as fragments
27722 * This is based loosely on tinymce
27723 * @class Roo.htmleditor.TidyWriter
27724 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27727 * - not tested much with 'PRE' formated elements.
27733 Roo.htmleditor.TidyWriter = function(settings)
27736 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27737 Roo.apply(this, settings);
27741 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27744 Roo.htmleditor.TidyWriter.prototype = {
27751 // part of state...
27755 last_inline : false,
27760 * Writes the a start element such as <p id="a">.
27763 * @param {String} name Name of the element.
27764 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27765 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27767 start: function(name, attrs, empty, node)
27769 var i, l, attr, value;
27771 // there are some situations where adding line break && indentation will not work. will not work.
27772 // <span / b / i ... formating?
27774 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27775 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27777 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27779 var add_lb = name == 'BR' ? false : in_inline;
27781 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27785 var indentstr = this.indentstr;
27787 // e_inline = elements that can be inline, but still allow \n before and after?
27788 // only 'BR' ??? any others?
27790 // ADD LINE BEFORE tage
27791 if (!this.in_pre) {
27794 if (name == 'BR') {
27796 } else if (this.lastElementEndsWS()) {
27799 // otherwise - no new line. (and dont indent.)
27810 this.html.push(indentstr + '<', name.toLowerCase());
27813 for (i = 0, l = attrs.length; i < l; i++) {
27815 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27821 this.html[this.html.length] = '/>';
27823 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27825 var e_inline = name == 'BR' ? false : this.in_inline;
27827 if (!e_inline && !this.in_pre) {
27834 this.html[this.html.length] = '>';
27836 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27838 if (!in_inline && !in_pre) {
27839 var cn = node.firstChild;
27841 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27845 cn = cn.nextSibling;
27853 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27855 in_inline : in_inline
27857 // add a line after if we are not in a
27859 if (!in_inline && !in_pre) {
27868 lastElementEndsWS : function()
27870 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27871 if (value === false) {
27874 return value.match(/\s+$/);
27879 * Writes the a end element such as </p>.
27882 * @param {String} name Name of the element.
27884 end: function(name) {
27887 var indentstr = '';
27888 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27890 if (!this.in_pre && !in_inline) {
27892 indentstr = this.indentstr;
27894 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27895 this.last_inline = in_inline;
27897 // pop the indent state..
27900 * Writes a text node.
27902 * In pre - we should not mess with the contents.
27906 * @param {String} text String to write out.
27907 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27909 text: function(in_text, node)
27911 // if not in whitespace critical
27912 if (in_text.length < 1) {
27915 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27918 this.html[this.html.length] = text;
27922 if (this.in_inline) {
27923 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27925 text = text.replace(/\s+/,' '); // all white space to single white space
27928 // if next tag is '<BR>', then we can trim right..
27929 if (node.nextSibling &&
27930 node.nextSibling.nodeType == 1 &&
27931 node.nextSibling.nodeName == 'BR' )
27933 text = text.replace(/\s+$/g,'');
27935 // if previous tag was a BR, we can also trim..
27936 if (node.previousSibling &&
27937 node.previousSibling.nodeType == 1 &&
27938 node.previousSibling.nodeName == 'BR' )
27940 text = this.indentstr + text.replace(/^\s+/g,'');
27942 if (text.match(/\n/)) {
27943 text = text.replace(
27944 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27946 // remoeve the last whitespace / line break.
27947 text = text.replace(/\n\s+$/,'');
27949 // repace long lines
27953 this.html[this.html.length] = text;
27956 // see if previous element was a inline element.
27957 var indentstr = this.indentstr;
27959 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27961 // should trim left?
27962 if (node.previousSibling &&
27963 node.previousSibling.nodeType == 1 &&
27964 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27970 text = text.replace(/^\s+/,''); // trim left
27973 // should trim right?
27974 if (node.nextSibling &&
27975 node.nextSibling.nodeType == 1 &&
27976 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27981 text = text.replace(/\s+$/,''); // trim right
27988 if (text.length < 1) {
27991 if (!text.match(/\n/)) {
27992 this.html.push(indentstr + text);
27996 text = this.indentstr + text.replace(
27997 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27999 // remoeve the last whitespace / line break.
28000 text = text.replace(/\s+$/,'');
28002 this.html.push(text);
28004 // split and indent..
28009 * Writes a cdata node such as <![CDATA[data]]>.
28012 * @param {String} text String to write out inside the cdata.
28014 cdata: function(text) {
28015 this.html.push('<![CDATA[', text, ']]>');
28018 * Writes a comment node such as <!-- Comment -->.
28021 * @param {String} text String to write out inside the comment.
28023 comment: function(text) {
28024 this.html.push('<!--', text, '-->');
28027 * Writes a PI node such as <?xml attr="value" ?>.
28030 * @param {String} name Name of the pi.
28031 * @param {String} text String to write out inside the pi.
28033 pi: function(name, text) {
28034 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28035 this.indent != '' && this.html.push('\n');
28038 * Writes a doctype node such as <!DOCTYPE data>.
28041 * @param {String} text String to write out inside the doctype.
28043 doctype: function(text) {
28044 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28047 * Resets the internal buffer if one wants to reuse the writer.
28051 reset: function() {
28052 this.html.length = 0;
28061 * Returns the contents that got serialized.
28063 * @method getContent
28064 * @return {String} HTML contents that got written down.
28066 getContent: function() {
28067 return this.html.join('').replace(/\n$/, '');
28070 pushState : function(cfg)
28072 this.state.push(cfg);
28073 Roo.apply(this, cfg);
28076 popState : function()
28078 if (this.state.length < 1) {
28079 return; // nothing to push
28086 if (this.state.length > 0) {
28087 cfg = this.state[this.state.length-1];
28089 Roo.apply(this, cfg);
28092 addLine: function()
28094 if (this.html.length < 1) {
28099 var value = this.html[this.html.length - 1];
28100 if (value.length > 0 && '\n' !== value) {
28101 this.html.push('\n');
28106 //'pre script noscript style textarea video audio iframe object code'
28107 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
28111 Roo.htmleditor.TidyWriter.inline_elements = [
28112 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28113 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28115 Roo.htmleditor.TidyWriter.shortend_elements = [
28116 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28117 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28120 Roo.htmleditor.TidyWriter.whitespace_elements = [
28121 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28123 * This is based loosely on tinymce
28124 * @class Roo.htmleditor.TidyEntities
28126 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28128 * Not 100% sure this is actually used or needed.
28131 Roo.htmleditor.TidyEntities = {
28134 * initialize data..
28136 init : function (){
28138 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28143 buildEntitiesLookup: function(items, radix) {
28144 var i, chr, entity, lookup = {};
28148 items = typeof(items) == 'string' ? items.split(',') : items;
28149 radix = radix || 10;
28150 // Build entities lookup table
28151 for (i = 0; i < items.length; i += 2) {
28152 chr = String.fromCharCode(parseInt(items[i], radix));
28153 // Only add non base entities
28154 if (!this.baseEntities[chr]) {
28155 entity = '&' + items[i + 1] + ';';
28156 lookup[chr] = entity;
28157 lookup[entity] = chr;
28196 // Needs to be escaped since the YUI compressor would otherwise break the code
28203 // Reverse lookup table for raw entities
28204 reverseEntities : {
28212 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28213 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28214 rawCharsRegExp : /[<>&\"\']/g,
28215 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28216 namedEntities : false,
28217 namedEntitiesData : [
28718 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28720 * @method encodeRaw
28721 * @param {String} text Text to encode.
28722 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28723 * @return {String} Entity encoded text.
28725 encodeRaw: function(text, attr)
28728 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28729 return t.baseEntities[chr] || chr;
28733 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28734 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28735 * and is exposed as the DOMUtils.encode function.
28737 * @method encodeAllRaw
28738 * @param {String} text Text to encode.
28739 * @return {String} Entity encoded text.
28741 encodeAllRaw: function(text) {
28743 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28744 return t.baseEntities[chr] || chr;
28748 * Encodes the specified string using numeric entities. The core entities will be
28749 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28751 * @method encodeNumeric
28752 * @param {String} text Text to encode.
28753 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28754 * @return {String} Entity encoded text.
28756 encodeNumeric: function(text, attr) {
28758 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28759 // Multi byte sequence convert it to a single entity
28760 if (chr.length > 1) {
28761 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28763 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28767 * Encodes the specified string using named entities. The core entities will be encoded
28768 * as named ones but all non lower ascii characters will be encoded into named entities.
28770 * @method encodeNamed
28771 * @param {String} text Text to encode.
28772 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28773 * @param {Object} entities Optional parameter with entities to use.
28774 * @return {String} Entity encoded text.
28776 encodeNamed: function(text, attr, entities) {
28778 entities = entities || this.namedEntities;
28779 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28780 return t.baseEntities[chr] || entities[chr] || chr;
28784 * Returns an encode function based on the name(s) and it's optional entities.
28786 * @method getEncodeFunc
28787 * @param {String} name Comma separated list of encoders for example named,numeric.
28788 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28789 * @return {function} Encode function to be used.
28791 getEncodeFunc: function(name, entities) {
28792 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28794 function encodeNamedAndNumeric(text, attr) {
28795 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28796 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28800 function encodeCustomNamed(text, attr) {
28801 return t.encodeNamed(text, attr, entities);
28803 // Replace + with , to be compatible with previous TinyMCE versions
28804 name = this.makeMap(name.replace(/\+/g, ','));
28805 // Named and numeric encoder
28806 if (name.named && name.numeric) {
28807 return this.encodeNamedAndNumeric;
28813 return encodeCustomNamed;
28815 return this.encodeNamed;
28818 if (name.numeric) {
28819 return this.encodeNumeric;
28822 return this.encodeRaw;
28825 * Decodes the specified string, this will replace entities with raw UTF characters.
28828 * @param {String} text Text to entity decode.
28829 * @return {String} Entity decoded string.
28831 decode: function(text)
28834 return text.replace(this.entityRegExp, function(all, numeric) {
28836 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28837 // Support upper UTF
28838 if (numeric > 65535) {
28840 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28842 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28844 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28847 nativeDecode : function (text) {
28850 makeMap : function (items, delim, map) {
28852 items = items || [];
28853 delim = delim || ',';
28854 if (typeof items == "string") {
28855 items = items.split(delim);
28860 map[items[i]] = {};
28868 Roo.htmleditor.TidyEntities.init();
28870 * @class Roo.htmleditor.KeyEnter
28871 * Handle Enter press..
28872 * @cfg {Roo.HtmlEditorCore} core the editor.
28874 * Create a new Filter.
28875 * @param {Object} config Configuration options
28882 Roo.htmleditor.KeyEnter = function(cfg) {
28883 Roo.apply(this, cfg);
28884 // this does not actually call walk as it's really just a abstract class
28886 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28889 //Roo.htmleditor.KeyEnter.i = 0;
28892 Roo.htmleditor.KeyEnter.prototype = {
28896 keypress : function(e)
28898 if (e.charCode != 13 && e.charCode != 10) {
28899 Roo.log([e.charCode,e]);
28902 e.preventDefault();
28903 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28904 var doc = this.core.doc;
28908 var sel = this.core.getSelection();
28909 var range = sel.getRangeAt(0);
28910 var n = range.commonAncestorContainer;
28911 var pc = range.closest([ 'ol', 'ul']);
28912 var pli = range.closest('li');
28913 if (!pc || e.ctrlKey) {
28914 // on it list, or ctrl pressed.
28916 sel.insertNode('br', 'after');
28918 // only do this if we have ctrl key..
28919 var br = doc.createElement('br');
28920 br.className = 'clear';
28921 br.setAttribute('style', 'clear: both');
28922 sel.insertNode(br, 'after');
28926 this.core.undoManager.addEvent();
28927 this.core.fireEditorEvent(e);
28931 // deal with <li> insetion
28932 if (pli.innerText.trim() == '' &&
28933 pli.previousSibling &&
28934 pli.previousSibling.nodeName == 'LI' &&
28935 pli.previousSibling.innerText.trim() == '') {
28936 pli.parentNode.removeChild(pli.previousSibling);
28937 sel.cursorAfter(pc);
28938 this.core.undoManager.addEvent();
28939 this.core.fireEditorEvent(e);
28943 var li = doc.createElement('LI');
28944 li.innerHTML = ' ';
28945 if (!pli || !pli.firstSibling) {
28946 pc.appendChild(li);
28948 pli.parentNode.insertBefore(li, pli.firstSibling);
28950 sel.cursorText (li.firstChild);
28952 this.core.undoManager.addEvent();
28953 this.core.fireEditorEvent(e);
28965 * @class Roo.htmleditor.Block
28966 * Base class for html editor blocks - do not use it directly .. extend it..
28967 * @cfg {DomElement} node The node to apply stuff to.
28968 * @cfg {String} friendly_name the name that appears in the context bar about this block
28969 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28972 * Create a new Filter.
28973 * @param {Object} config Configuration options
28976 Roo.htmleditor.Block = function(cfg)
28978 // do nothing .. should not be called really.
28981 * factory method to get the block from an element (using cache if necessary)
28983 * @param {HtmlElement} the dom element
28985 Roo.htmleditor.Block.factory = function(node)
28987 var cc = Roo.htmleditor.Block.cache;
28988 var id = Roo.get(node).id;
28989 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28990 Roo.htmleditor.Block.cache[id].readElement(node);
28991 return Roo.htmleditor.Block.cache[id];
28993 var db = node.getAttribute('data-block');
28995 db = node.nodeName.toLowerCase().toUpperCaseFirst();
28997 var cls = Roo.htmleditor['Block' + db];
28998 if (typeof(cls) == 'undefined') {
28999 //Roo.log(node.getAttribute('data-block'));
29000 Roo.log("OOps missing block : " + 'Block' + db);
29003 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29004 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
29008 * initalize all Elements from content that are 'blockable'
29010 * @param the body element
29012 Roo.htmleditor.Block.initAll = function(body, type)
29014 if (typeof(type) == 'undefined') {
29015 var ia = Roo.htmleditor.Block.initAll;
29021 Roo.each(Roo.get(body).query(type), function(e) {
29022 Roo.htmleditor.Block.factory(e);
29025 // question goes here... do we need to clear out this cache sometimes?
29026 // or show we make it relivant to the htmleditor.
29027 Roo.htmleditor.Block.cache = {};
29029 Roo.htmleditor.Block.prototype = {
29033 // used by context menu
29034 friendly_name : 'Based Block',
29036 // text for button to delete this element
29037 deleteTitle : false,
29041 * Update a node with values from this object
29042 * @param {DomElement} node
29044 updateElement : function(node)
29046 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29049 * convert to plain HTML for calling insertAtCursor..
29051 toHTML : function()
29053 return Roo.DomHelper.markup(this.toObject());
29056 * used by readEleemnt to extract data from a node
29057 * may need improving as it's pretty basic
29059 * @param {DomElement} node
29060 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29061 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29062 * @param {String} style the style property - eg. text-align
29064 getVal : function(node, tag, attr, style)
29067 if (tag !== true && n.tagName != tag.toUpperCase()) {
29068 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29069 // but kiss for now.
29070 n = node.getElementsByTagName(tag).item(0);
29075 if (attr === false) {
29078 if (attr == 'html') {
29079 return n.innerHTML;
29081 if (attr == 'style') {
29082 return n.style[style];
29085 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29089 * create a DomHelper friendly object - for use with
29090 * Roo.DomHelper.markup / overwrite / etc..
29093 toObject : function()
29098 * Read a node that has a 'data-block' property - and extract the values from it.
29099 * @param {DomElement} node - the node
29101 readElement : function(node)
29112 * @class Roo.htmleditor.BlockFigure
29113 * Block that has an image and a figcaption
29114 * @cfg {String} image_src the url for the image
29115 * @cfg {String} align (left|right) alignment for the block default left
29116 * @cfg {String} caption the text to appear below (and in the alt tag)
29117 * @cfg {String} caption_display (block|none) display or not the caption
29118 * @cfg {String|number} image_width the width of the image number or %?
29119 * @cfg {String|number} image_height the height of the image number or %?
29122 * Create a new Filter.
29123 * @param {Object} config Configuration options
29126 Roo.htmleditor.BlockFigure = function(cfg)
29129 this.readElement(cfg.node);
29130 this.updateElement(cfg.node);
29132 Roo.apply(this, cfg);
29134 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29141 caption_display : 'block',
29147 // margin: '2%', not used
29149 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
29152 // used by context menu
29153 friendly_name : 'Image with caption',
29154 deleteTitle : "Delete Image and Caption",
29156 contextMenu : function(toolbar)
29159 var block = function() {
29160 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29164 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29166 var syncValue = toolbar.editorcore.syncValue;
29172 xtype : 'TextItem',
29174 xns : rooui.Toolbar //Boostrap?
29178 text: 'Change Image URL',
29181 click: function (btn, state)
29185 Roo.MessageBox.show({
29186 title : "Image Source URL",
29187 msg : "Enter the url for the image",
29188 buttons: Roo.MessageBox.OKCANCEL,
29189 fn: function(btn, val){
29196 toolbar.editorcore.onEditorEvent();
29200 //multiline: multiline,
29202 value : b.image_src
29206 xns : rooui.Toolbar
29211 text: 'Change Link URL',
29214 click: function (btn, state)
29218 Roo.MessageBox.show({
29219 title : "Link URL",
29220 msg : "Enter the url for the link - leave blank to have no link",
29221 buttons: Roo.MessageBox.OKCANCEL,
29222 fn: function(btn, val){
29229 toolbar.editorcore.onEditorEvent();
29233 //multiline: multiline,
29239 xns : rooui.Toolbar
29243 text: 'Show Video URL',
29246 click: function (btn, state)
29248 Roo.MessageBox.alert("Video URL",
29249 block().video_url == '' ? 'This image is not linked ot a video' :
29250 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29253 xns : rooui.Toolbar
29258 xtype : 'TextItem',
29260 xns : rooui.Toolbar //Boostrap?
29263 xtype : 'ComboBox',
29264 allowBlank : false,
29265 displayField : 'val',
29268 triggerAction : 'all',
29270 valueField : 'val',
29274 select : function (combo, r, index)
29276 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29278 b.width = r.get('val');
29281 toolbar.editorcore.onEditorEvent();
29286 xtype : 'SimpleStore',
29299 xtype : 'TextItem',
29301 xns : rooui.Toolbar //Boostrap?
29304 xtype : 'ComboBox',
29305 allowBlank : false,
29306 displayField : 'val',
29309 triggerAction : 'all',
29311 valueField : 'val',
29315 select : function (combo, r, index)
29317 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29319 b.align = r.get('val');
29322 toolbar.editorcore.onEditorEvent();
29327 xtype : 'SimpleStore',
29341 text: 'Hide Caption',
29342 name : 'caption_display',
29344 enableToggle : true,
29345 setValue : function(v) {
29346 // this trigger toggle.
29348 this.setText(v ? "Hide Caption" : "Show Caption");
29349 this.setPressed(v != 'block');
29352 toggle: function (btn, state)
29355 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29356 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29359 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29360 toolbar.editorcore.onEditorEvent();
29363 xns : rooui.Toolbar
29369 * create a DomHelper friendly object - for use with
29370 * Roo.DomHelper.markup / overwrite / etc..
29372 toObject : function()
29374 var d = document.createElement('div');
29375 d.innerHTML = this.caption;
29377 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
29379 var iw = this.align == 'center' ? this.width : '100%';
29382 contenteditable : 'false',
29383 src : this.image_src,
29384 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29387 maxWidth : iw + ' !important', // this is not getting rendered?
29393 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29395 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
29400 if (this.href.length > 0) {
29404 contenteditable : 'true',
29412 if (this.video_url.length > 0) {
29417 allowfullscreen : true,
29418 width : 420, // these are for video tricks - that we replace the outer
29420 src : this.video_url,
29426 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29427 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29432 'data-block' : 'Figure',
29433 'data-width' : this.width,
29434 contenteditable : 'false',
29438 float : this.align ,
29439 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29440 width : this.align == 'center' ? '100%' : this.width,
29442 padding: this.align == 'center' ? '0' : '0 10px' ,
29443 textAlign : this.align // seems to work for email..
29448 align : this.align,
29454 'data-display' : this.caption_display,
29456 textAlign : 'left',
29458 lineHeight : '24px',
29459 display : this.caption_display,
29460 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
29462 width: this.align == 'center' ? this.width : '100%'
29466 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
29471 marginTop : '16px',
29477 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
29479 contenteditable : true,
29495 readElement : function(node)
29497 // this should not really come from the link...
29498 this.video_url = this.getVal(node, 'div', 'src');
29499 this.cls = this.getVal(node, 'div', 'class');
29500 this.href = this.getVal(node, 'a', 'href');
29503 this.image_src = this.getVal(node, 'img', 'src');
29505 this.align = this.getVal(node, 'figure', 'align');
29506 var figcaption = this.getVal(node, 'figcaption', false);
29507 if (figcaption !== '') {
29508 this.caption = this.getVal(figcaption, 'i', 'html');
29512 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29513 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29514 this.width = this.getVal(node, true, 'data-width');
29515 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29518 removeNode : function()
29535 * @class Roo.htmleditor.BlockTable
29536 * Block that manages a table
29539 * Create a new Filter.
29540 * @param {Object} config Configuration options
29543 Roo.htmleditor.BlockTable = function(cfg)
29546 this.readElement(cfg.node);
29547 this.updateElement(cfg.node);
29549 Roo.apply(this, cfg);
29552 for(var r = 0; r < this.no_row; r++) {
29554 for(var c = 0; c < this.no_col; c++) {
29555 this.rows[r][c] = this.emptyCell();
29562 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29571 // used by context menu
29572 friendly_name : 'Table',
29573 deleteTitle : 'Delete Table',
29574 // context menu is drawn once..
29576 contextMenu : function(toolbar)
29579 var block = function() {
29580 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29584 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29586 var syncValue = toolbar.editorcore.syncValue;
29592 xtype : 'TextItem',
29594 xns : rooui.Toolbar //Boostrap?
29597 xtype : 'ComboBox',
29598 allowBlank : false,
29599 displayField : 'val',
29602 triggerAction : 'all',
29604 valueField : 'val',
29608 select : function (combo, r, index)
29610 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29612 b.width = r.get('val');
29615 toolbar.editorcore.onEditorEvent();
29620 xtype : 'SimpleStore',
29632 xtype : 'TextItem',
29633 text : "Columns: ",
29634 xns : rooui.Toolbar //Boostrap?
29641 click : function (_self, e)
29643 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29644 block().removeColumn();
29646 toolbar.editorcore.onEditorEvent();
29649 xns : rooui.Toolbar
29655 click : function (_self, e)
29657 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29658 block().addColumn();
29660 toolbar.editorcore.onEditorEvent();
29663 xns : rooui.Toolbar
29667 xtype : 'TextItem',
29669 xns : rooui.Toolbar //Boostrap?
29676 click : function (_self, e)
29678 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29679 block().removeRow();
29681 toolbar.editorcore.onEditorEvent();
29684 xns : rooui.Toolbar
29690 click : function (_self, e)
29694 toolbar.editorcore.onEditorEvent();
29697 xns : rooui.Toolbar
29702 text: 'Reset Column Widths',
29705 click : function (_self, e)
29707 block().resetWidths();
29709 toolbar.editorcore.onEditorEvent();
29712 xns : rooui.Toolbar
29723 * create a DomHelper friendly object - for use with
29724 * Roo.DomHelper.markup / overwrite / etc..
29725 * ?? should it be called with option to hide all editing features?
29727 toObject : function()
29732 contenteditable : 'false', // this stops cell selection from picking the table.
29733 'data-block' : 'Table',
29736 border : 'solid 1px #000', // ??? hard coded?
29737 'border-collapse' : 'collapse'
29740 { tag : 'tbody' , cn : [] }
29744 // do we have a head = not really
29746 Roo.each(this.rows, function( row ) {
29751 border : 'solid 1px #000',
29757 ret.cn[0].cn.push(tr);
29758 // does the row have any properties? ?? height?
29760 Roo.each(row, function( cell ) {
29764 contenteditable : 'true',
29765 'data-block' : 'Td',
29769 if (cell.colspan > 1) {
29770 td.colspan = cell.colspan ;
29771 nc += cell.colspan;
29775 if (cell.rowspan > 1) {
29776 td.rowspan = cell.rowspan ;
29785 ncols = Math.max(nc, ncols);
29789 // add the header row..
29798 readElement : function(node)
29800 node = node ? node : this.node ;
29801 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29805 var trs = Array.from(node.rows);
29806 trs.forEach(function(tr) {
29808 this.rows.push(row);
29812 Array.from(tr.cells).forEach(function(td) {
29815 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29816 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29817 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29818 html : td.innerHTML
29820 no_column += add.colspan;
29827 this.no_col = Math.max(this.no_col, no_column);
29834 normalizeRows: function()
29838 this.rows.forEach(function(row) {
29841 row = this.normalizeRow(row);
29843 row.forEach(function(c) {
29844 while (typeof(ret[rid][cid]) != 'undefined') {
29847 if (typeof(ret[rid]) == 'undefined') {
29853 if (c.rowspan < 2) {
29857 for(var i = 1 ;i < c.rowspan; i++) {
29858 if (typeof(ret[rid+i]) == 'undefined') {
29861 ret[rid+i][cid] = c;
29869 normalizeRow: function(row)
29872 row.forEach(function(c) {
29873 if (c.colspan < 2) {
29877 for(var i =0 ;i < c.colspan; i++) {
29885 deleteColumn : function(sel)
29887 if (!sel || sel.type != 'col') {
29890 if (this.no_col < 2) {
29894 this.rows.forEach(function(row) {
29895 var cols = this.normalizeRow(row);
29896 var col = cols[sel.col];
29897 if (col.colspan > 1) {
29907 removeColumn : function()
29909 this.deleteColumn({
29911 col : this.no_col-1
29913 this.updateElement();
29917 addColumn : function()
29920 this.rows.forEach(function(row) {
29921 row.push(this.emptyCell());
29924 this.updateElement();
29927 deleteRow : function(sel)
29929 if (!sel || sel.type != 'row') {
29933 if (this.no_row < 2) {
29937 var rows = this.normalizeRows();
29940 rows[sel.row].forEach(function(col) {
29941 if (col.rowspan > 1) {
29944 col.remove = 1; // flage it as removed.
29949 this.rows.forEach(function(row) {
29951 row.forEach(function(c) {
29952 if (typeof(c.remove) == 'undefined') {
29957 if (newrow.length > 0) {
29961 this.rows = newrows;
29966 this.updateElement();
29969 removeRow : function()
29973 row : this.no_row-1
29979 addRow : function()
29983 for (var i = 0; i < this.no_col; i++ ) {
29985 row.push(this.emptyCell());
29988 this.rows.push(row);
29989 this.updateElement();
29993 // the default cell object... at present...
29994 emptyCell : function() {
29995 return (new Roo.htmleditor.BlockTd({})).toObject();
30000 removeNode : function()
30007 resetWidths : function()
30009 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30010 var nn = Roo.htmleditor.Block.factory(n);
30012 nn.updateElement(n);
30025 * since selections really work on the table cell, then editing really should work from there
30027 * The original plan was to support merging etc... - but that may not be needed yet..
30029 * So this simple version will support:
30031 * adjust the width +/-
30032 * reset the width...
30041 * @class Roo.htmleditor.BlockTable
30042 * Block that manages a table
30045 * Create a new Filter.
30046 * @param {Object} config Configuration options
30049 Roo.htmleditor.BlockTd = function(cfg)
30052 this.readElement(cfg.node);
30053 this.updateElement(cfg.node);
30055 Roo.apply(this, cfg);
30060 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30065 textAlign : 'left',
30072 // used by context menu
30073 friendly_name : 'Table Cell',
30074 deleteTitle : false, // use our customer delete
30076 // context menu is drawn once..
30078 contextMenu : function(toolbar)
30081 var cell = function() {
30082 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30085 var table = function() {
30086 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30090 var saveSel = function()
30092 lr = toolbar.editorcore.getSelection().getRangeAt(0);
30094 var restoreSel = function()
30098 toolbar.editorcore.focus();
30099 var cr = toolbar.editorcore.getSelection();
30100 cr.removeAllRanges();
30102 toolbar.editorcore.onEditorEvent();
30103 }).defer(10, this);
30109 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30111 var syncValue = toolbar.editorcore.syncValue;
30118 text : 'Edit Table',
30120 click : function() {
30121 var t = toolbar.tb.selectedNode.closest('table');
30122 toolbar.editorcore.selectNode(t);
30123 toolbar.editorcore.onEditorEvent();
30132 xtype : 'TextItem',
30133 text : "Column Width: ",
30134 xns : rooui.Toolbar
30141 click : function (_self, e)
30143 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30144 cell().shrinkColumn();
30146 toolbar.editorcore.onEditorEvent();
30149 xns : rooui.Toolbar
30155 click : function (_self, e)
30157 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30158 cell().growColumn();
30160 toolbar.editorcore.onEditorEvent();
30163 xns : rooui.Toolbar
30167 xtype : 'TextItem',
30168 text : "Vertical Align: ",
30169 xns : rooui.Toolbar //Boostrap?
30172 xtype : 'ComboBox',
30173 allowBlank : false,
30174 displayField : 'val',
30177 triggerAction : 'all',
30179 valueField : 'val',
30183 select : function (combo, r, index)
30185 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30187 b.valign = r.get('val');
30190 toolbar.editorcore.onEditorEvent();
30195 xtype : 'SimpleStore',
30199 ['bottom'] // there are afew more...
30207 xtype : 'TextItem',
30208 text : "Merge Cells: ",
30209 xns : rooui.Toolbar
30218 click : function (_self, e)
30220 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30221 cell().mergeRight();
30222 //block().growColumn();
30224 toolbar.editorcore.onEditorEvent();
30227 xns : rooui.Toolbar
30234 click : function (_self, e)
30236 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30237 cell().mergeBelow();
30238 //block().growColumn();
30240 toolbar.editorcore.onEditorEvent();
30243 xns : rooui.Toolbar
30246 xtype : 'TextItem',
30248 xns : rooui.Toolbar
30256 click : function (_self, e)
30258 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30261 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30262 toolbar.editorcore.onEditorEvent();
30266 xns : rooui.Toolbar
30270 xns : rooui.Toolbar
30279 xns : rooui.Toolbar,
30288 click : function (_self, e)
30292 cell().deleteColumn();
30294 toolbar.editorcore.selectNode(t.node);
30295 toolbar.editorcore.onEditorEvent();
30304 click : function (_self, e)
30307 cell().deleteRow();
30310 toolbar.editorcore.selectNode(t.node);
30311 toolbar.editorcore.onEditorEvent();
30318 xtype : 'Separator',
30325 click : function (_self, e)
30328 var nn = t.node.nextSibling || t.node.previousSibling;
30329 t.node.parentNode.removeChild(t.node);
30331 toolbar.editorcore.selectNode(nn, true);
30333 toolbar.editorcore.onEditorEvent();
30343 // align... << fixme
30351 * create a DomHelper friendly object - for use with
30352 * Roo.DomHelper.markup / overwrite / etc..
30353 * ?? should it be called with option to hide all editing features?
30356 * create a DomHelper friendly object - for use with
30357 * Roo.DomHelper.markup / overwrite / etc..
30358 * ?? should it be called with option to hide all editing features?
30360 toObject : function()
30364 contenteditable : 'true', // this stops cell selection from picking the table.
30365 'data-block' : 'Td',
30366 valign : this.valign,
30368 'text-align' : this.textAlign,
30369 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30370 'border-collapse' : 'collapse',
30371 padding : '6px', // 8 for desktop / 4 for mobile
30372 'vertical-align': this.valign
30376 if (this.width != '') {
30377 ret.width = this.width;
30378 ret.style.width = this.width;
30382 if (this.colspan > 1) {
30383 ret.colspan = this.colspan ;
30385 if (this.rowspan > 1) {
30386 ret.rowspan = this.rowspan ;
30395 readElement : function(node)
30397 node = node ? node : this.node ;
30398 this.width = node.style.width;
30399 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30400 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30401 this.html = node.innerHTML;
30402 if (node.style.textAlign != '') {
30403 this.textAlign = node.style.textAlign;
30409 // the default cell object... at present...
30410 emptyCell : function() {
30414 textAlign : 'left',
30415 html : " " // is this going to be editable now?
30420 removeNode : function()
30422 return this.node.closest('table');
30430 toTableArray : function()
30433 var tab = this.node.closest('tr').closest('table');
30434 Array.from(tab.rows).forEach(function(r, ri){
30438 this.colWidths = [];
30439 var all_auto = true;
30440 Array.from(tab.rows).forEach(function(r, ri){
30443 Array.from(r.cells).forEach(function(ce, ci){
30448 colspan : ce.colSpan,
30449 rowspan : ce.rowSpan
30451 if (ce.isEqualNode(this.node)) {
30454 // if we have been filled up by a row?
30455 if (typeof(ret[rn][cn]) != 'undefined') {
30456 while(typeof(ret[rn][cn]) != 'undefined') {
30462 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30463 this.colWidths[cn] = ce.style.width;
30464 if (this.colWidths[cn] != '') {
30470 if (c.colspan < 2 && c.rowspan < 2 ) {
30475 for(var j = 0; j < c.rowspan; j++) {
30476 if (typeof(ret[rn+j]) == 'undefined') {
30477 continue; // we have a problem..
30480 for(var i = 0; i < c.colspan; i++) {
30481 ret[rn+j][cn+i] = c;
30490 // initalize widths.?
30491 // either all widths or no widths..
30493 this.colWidths[0] = false; // no widths flag.
30504 mergeRight: function()
30507 // get the contents of the next cell along..
30508 var tr = this.node.closest('tr');
30509 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30510 if (i >= tr.childNodes.length - 1) {
30511 return; // no cells on right to merge with.
30513 var table = this.toTableArray();
30515 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30516 return; // nothing right?
30518 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30519 // right cell - must be same rowspan and on the same row.
30520 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30521 return; // right hand side is not same rowspan.
30526 this.node.innerHTML += ' ' + rc.cell.innerHTML;
30527 tr.removeChild(rc.cell);
30528 this.colspan += rc.colspan;
30529 this.node.setAttribute('colspan', this.colspan);
30531 var table = this.toTableArray();
30532 this.normalizeWidths(table);
30533 this.updateWidths(table);
30537 mergeBelow : function()
30539 var table = this.toTableArray();
30540 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30541 return; // no row below
30543 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30544 return; // nothing right?
30546 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30548 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30549 return; // right hand side is not same rowspan.
30551 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30552 rc.cell.parentNode.removeChild(rc.cell);
30553 this.rowspan += rc.rowspan;
30554 this.node.setAttribute('rowspan', this.rowspan);
30559 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30562 var table = this.toTableArray();
30563 var cd = this.cellData;
30567 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30570 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30571 if (r == cd.row && c == cd.col) {
30572 this.node.removeAttribute('rowspan');
30573 this.node.removeAttribute('colspan');
30576 var ntd = this.node.cloneNode(); // which col/row should be 0..
30577 ntd.removeAttribute('id');
30578 ntd.style.width = this.colWidths[c];
30579 ntd.innerHTML = '';
30580 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30584 this.redrawAllCells(table);
30590 redrawAllCells: function(table)
30594 var tab = this.node.closest('tr').closest('table');
30595 var ctr = tab.rows[0].parentNode;
30596 Array.from(tab.rows).forEach(function(r, ri){
30598 Array.from(r.cells).forEach(function(ce, ci){
30599 ce.parentNode.removeChild(ce);
30601 r.parentNode.removeChild(r);
30603 for(var r = 0 ; r < table.length; r++) {
30604 var re = tab.rows[r];
30606 var re = tab.ownerDocument.createElement('tr');
30607 ctr.appendChild(re);
30608 for(var c = 0 ; c < table[r].length; c++) {
30609 if (table[r][c].cell === false) {
30613 re.appendChild(table[r][c].cell);
30615 table[r][c].cell = false;
30620 updateWidths : function(table)
30622 for(var r = 0 ; r < table.length; r++) {
30624 for(var c = 0 ; c < table[r].length; c++) {
30625 if (table[r][c].cell === false) {
30629 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30630 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30631 el.width = Math.floor(this.colWidths[c]) +'%';
30632 el.updateElement(el.node);
30634 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30635 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30637 for(var i = 0; i < table[r][c].colspan; i ++) {
30638 width += Math.floor(this.colWidths[c + i]);
30640 el.width = width +'%';
30641 el.updateElement(el.node);
30643 table[r][c].cell = false; // done
30647 normalizeWidths : function(table)
30649 if (this.colWidths[0] === false) {
30650 var nw = 100.0 / this.colWidths.length;
30651 this.colWidths.forEach(function(w,i) {
30652 this.colWidths[i] = nw;
30657 var t = 0, missing = [];
30659 this.colWidths.forEach(function(w,i) {
30661 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30662 var add = this.colWidths[i];
30671 var nc = this.colWidths.length;
30672 if (missing.length) {
30673 var mult = (nc - missing.length) / (1.0 * nc);
30675 var ew = (100 -t) / (1.0 * missing.length);
30676 this.colWidths.forEach(function(w,i) {
30678 this.colWidths[i] = w * mult;
30682 this.colWidths[i] = ew;
30684 // have to make up numbers..
30687 // now we should have all the widths..
30692 shrinkColumn : function()
30694 var table = this.toTableArray();
30695 this.normalizeWidths(table);
30696 var col = this.cellData.col;
30697 var nw = this.colWidths[col] * 0.8;
30701 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30702 this.colWidths.forEach(function(w,i) {
30704 this.colWidths[i] = nw;
30707 this.colWidths[i] += otherAdd
30709 this.updateWidths(table);
30712 growColumn : function()
30714 var table = this.toTableArray();
30715 this.normalizeWidths(table);
30716 var col = this.cellData.col;
30717 var nw = this.colWidths[col] * 1.2;
30721 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30722 this.colWidths.forEach(function(w,i) {
30724 this.colWidths[i] = nw;
30727 this.colWidths[i] -= otherSub
30729 this.updateWidths(table);
30732 deleteRow : function()
30734 // delete this rows 'tr'
30735 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30736 // then reduce the rowspan.
30737 var table = this.toTableArray();
30738 // this.cellData.row;
30739 for (var i =0;i< table[this.cellData.row].length ; i++) {
30740 var c = table[this.cellData.row][i];
30741 if (c.row != this.cellData.row) {
30744 c.cell.setAttribute('rowspan', c.rowspan);
30747 if (c.rowspan > 1) {
30749 c.cell.setAttribute('rowspan', c.rowspan);
30752 table.splice(this.cellData.row,1);
30753 this.redrawAllCells(table);
30756 deleteColumn : function()
30758 var table = this.toTableArray();
30760 for (var i =0;i< table.length ; i++) {
30761 var c = table[i][this.cellData.col];
30762 if (c.col != this.cellData.col) {
30763 table[i][this.cellData.col].colspan--;
30764 } else if (c.colspan > 1) {
30766 c.cell.setAttribute('colspan', c.colspan);
30768 table[i].splice(this.cellData.col,1);
30771 this.redrawAllCells(table);
30779 //<script type="text/javascript">
30782 * Based Ext JS Library 1.1.1
30783 * Copyright(c) 2006-2007, Ext JS, LLC.
30789 * @class Roo.HtmlEditorCore
30790 * @extends Roo.Component
30791 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30793 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30796 Roo.HtmlEditorCore = function(config){
30799 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30804 * @event initialize
30805 * Fires when the editor is fully initialized (including the iframe)
30806 * @param {Roo.HtmlEditorCore} this
30811 * Fires when the editor is first receives the focus. Any insertion must wait
30812 * until after this event.
30813 * @param {Roo.HtmlEditorCore} this
30817 * @event beforesync
30818 * Fires before the textarea is updated with content from the editor iframe. Return false
30819 * to cancel the sync.
30820 * @param {Roo.HtmlEditorCore} this
30821 * @param {String} html
30825 * @event beforepush
30826 * Fires before the iframe editor is updated with content from the textarea. Return false
30827 * to cancel the push.
30828 * @param {Roo.HtmlEditorCore} this
30829 * @param {String} html
30834 * Fires when the textarea is updated with content from the editor iframe.
30835 * @param {Roo.HtmlEditorCore} this
30836 * @param {String} html
30841 * Fires when the iframe editor is updated with content from the textarea.
30842 * @param {Roo.HtmlEditorCore} this
30843 * @param {String} html
30848 * @event editorevent
30849 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30850 * @param {Roo.HtmlEditorCore} this
30857 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30859 // defaults : white / black...
30860 this.applyBlacklists();
30867 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30871 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30877 * @cfg {String} css styling for resizing. (used on bootstrap only)
30881 * @cfg {Number} height (in pixels)
30885 * @cfg {Number} width (in pixels)
30889 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30890 * if you are doing an email editor, this probably needs disabling, it's designed
30895 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30897 enableBlocks : true,
30899 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30902 stylesheets: false,
30904 * @cfg {String} language default en - language of text (usefull for rtl languages)
30910 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30911 * - by default they are stripped - if you are editing email you may need this.
30913 allowComments: false,
30917 // private properties
30918 validationEvent : false,
30920 initialized : false,
30922 sourceEditMode : false,
30923 onFocus : Roo.emptyFn,
30925 hideMode:'offsets',
30929 // blacklist + whitelisted elements..
30936 undoManager : false,
30938 * Protected method that will not generally be called directly. It
30939 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30940 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30942 getDocMarkup : function(){
30946 // inherit styels from page...??
30947 if (this.stylesheets === false) {
30949 Roo.get(document.head).select('style').each(function(node) {
30950 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30953 Roo.get(document.head).select('link').each(function(node) {
30954 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30957 } else if (!this.stylesheets.length) {
30959 st = '<style type="text/css">' +
30960 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30963 for (var i in this.stylesheets) {
30964 if (typeof(this.stylesheets[i]) != 'string') {
30967 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30972 st += '<style type="text/css">' +
30973 'IMG { cursor: pointer } ' +
30976 st += '<meta name="google" content="notranslate">';
30978 var cls = 'notranslate roo-htmleditor-body';
30980 if(this.bodyCls.length){
30981 cls += ' ' + this.bodyCls;
30984 return '<html class="notranslate" translate="no"><head>' + st +
30985 //<style type="text/css">' +
30986 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30988 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
30992 onRender : function(ct, position)
30995 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30996 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30999 this.el.dom.style.border = '0 none';
31000 this.el.dom.setAttribute('tabIndex', -1);
31001 this.el.addClass('x-hidden hide');
31005 if(Roo.isIE){ // fix IE 1px bogus margin
31006 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31010 this.frameId = Roo.id();
31014 cls: 'form-control', // bootstrap..
31016 name: this.frameId,
31017 frameBorder : 'no',
31018 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
31021 ifcfg.style = { resize : this.resize };
31024 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
31027 this.iframe = iframe.dom;
31029 this.assignDocWin();
31031 this.doc.designMode = 'on';
31034 this.doc.write(this.getDocMarkup());
31038 var task = { // must defer to wait for browser to be ready
31040 //console.log("run task?" + this.doc.readyState);
31041 this.assignDocWin();
31042 if(this.doc.body || this.doc.readyState == 'complete'){
31044 this.doc.designMode="on";
31049 Roo.TaskMgr.stop(task);
31050 this.initEditor.defer(10, this);
31057 Roo.TaskMgr.start(task);
31062 onResize : function(w, h)
31064 Roo.log('resize: ' +w + ',' + h );
31065 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31069 if(typeof w == 'number'){
31071 this.iframe.style.width = w + 'px';
31073 if(typeof h == 'number'){
31075 this.iframe.style.height = h + 'px';
31077 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31084 * Toggles the editor between standard and source edit mode.
31085 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31087 toggleSourceEdit : function(sourceEditMode){
31089 this.sourceEditMode = sourceEditMode === true;
31091 if(this.sourceEditMode){
31093 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
31096 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31097 //this.iframe.className = '';
31100 //this.setSize(this.owner.wrap.getSize());
31101 //this.fireEvent('editmodechange', this, this.sourceEditMode);
31108 * Protected method that will not generally be called directly. If you need/want
31109 * custom HTML cleanup, this is the method you should override.
31110 * @param {String} html The HTML to be cleaned
31111 * return {String} The cleaned HTML
31113 cleanHtml : function(html)
31115 html = String(html);
31116 if(html.length > 5){
31117 if(Roo.isSafari){ // strip safari nonsense
31118 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31121 if(html == ' '){
31128 * HTML Editor -> Textarea
31129 * Protected method that will not generally be called directly. Syncs the contents
31130 * of the editor iframe with the textarea.
31132 syncValue : function()
31134 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31135 if(this.initialized){
31137 if (this.undoManager) {
31138 this.undoManager.addEvent();
31142 var bd = (this.doc.body || this.doc.documentElement);
31145 var sel = this.win.getSelection();
31147 var div = document.createElement('div');
31148 div.innerHTML = bd.innerHTML;
31149 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31150 if (gtx.length > 0) {
31151 var rm = gtx.item(0).parentNode;
31152 rm.parentNode.removeChild(rm);
31156 if (this.enableBlocks) {
31157 new Roo.htmleditor.FilterBlock({ node : div });
31160 var html = div.innerHTML;
31163 if (this.autoClean) {
31165 new Roo.htmleditor.FilterAttributes({
31186 attrib_clean : ['href', 'src' ]
31189 var tidy = new Roo.htmleditor.TidySerializer({
31192 html = tidy.serialize(div);
31198 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31199 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31201 html = '<div style="'+m[0]+'">' + html + '</div>';
31204 html = this.cleanHtml(html);
31205 // fix up the special chars.. normaly like back quotes in word...
31206 // however we do not want to do this with chinese..
31207 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31209 var cc = match.charCodeAt();
31211 // Get the character value, handling surrogate pairs
31212 if (match.length == 2) {
31213 // It's a surrogate pair, calculate the Unicode code point
31214 var high = match.charCodeAt(0) - 0xD800;
31215 var low = match.charCodeAt(1) - 0xDC00;
31216 cc = (high * 0x400) + low + 0x10000;
31218 (cc >= 0x4E00 && cc < 0xA000 ) ||
31219 (cc >= 0x3400 && cc < 0x4E00 ) ||
31220 (cc >= 0xf900 && cc < 0xfb00 )
31225 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31226 return "&#" + cc + ";";
31233 if(this.owner.fireEvent('beforesync', this, html) !== false){
31234 this.el.dom.value = html;
31235 this.owner.fireEvent('sync', this, html);
31241 * TEXTAREA -> EDITABLE
31242 * Protected method that will not generally be called directly. Pushes the value of the textarea
31243 * into the iframe editor.
31245 pushValue : function()
31247 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31248 if(this.initialized){
31249 var v = this.el.dom.value.trim();
31252 if(this.owner.fireEvent('beforepush', this, v) !== false){
31253 var d = (this.doc.body || this.doc.documentElement);
31256 this.el.dom.value = d.innerHTML;
31257 this.owner.fireEvent('push', this, v);
31259 if (this.autoClean) {
31260 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31261 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31263 if (this.enableBlocks) {
31264 Roo.htmleditor.Block.initAll(this.doc.body);
31267 this.updateLanguage();
31269 var lc = this.doc.body.lastChild;
31270 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31271 // add an extra line at the end.
31272 this.doc.body.appendChild(this.doc.createElement('br'));
31280 deferFocus : function(){
31281 this.focus.defer(10, this);
31285 focus : function(){
31286 if(this.win && !this.sourceEditMode){
31293 assignDocWin: function()
31295 var iframe = this.iframe;
31298 this.doc = iframe.contentWindow.document;
31299 this.win = iframe.contentWindow;
31301 // if (!Roo.get(this.frameId)) {
31304 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31305 // this.win = Roo.get(this.frameId).dom.contentWindow;
31307 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31311 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31312 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31317 initEditor : function(){
31318 //console.log("INIT EDITOR");
31319 this.assignDocWin();
31323 this.doc.designMode="on";
31325 this.doc.write(this.getDocMarkup());
31328 var dbody = (this.doc.body || this.doc.documentElement);
31329 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31330 // this copies styles from the containing element into thsi one..
31331 // not sure why we need all of this..
31332 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31334 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31335 //ss['background-attachment'] = 'fixed'; // w3c
31336 dbody.bgProperties = 'fixed'; // ie
31337 dbody.setAttribute("translate", "no");
31339 //Roo.DomHelper.applyStyles(dbody, ss);
31340 Roo.EventManager.on(this.doc, {
31342 'mouseup': this.onEditorEvent,
31343 'dblclick': this.onEditorEvent,
31344 'click': this.onEditorEvent,
31345 'keyup': this.onEditorEvent,
31350 Roo.EventManager.on(this.doc, {
31351 'paste': this.onPasteEvent,
31355 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31358 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31359 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31361 this.initialized = true;
31364 // initialize special key events - enter
31365 new Roo.htmleditor.KeyEnter({core : this});
31369 this.owner.fireEvent('initialize', this);
31372 // this is to prevent a href clicks resulting in a redirect?
31374 onPasteEvent : function(e,v)
31376 // I think we better assume paste is going to be a dirty load of rubish from word..
31378 // even pasting into a 'email version' of this widget will have to clean up that mess.
31379 var cd = (e.browserEvent.clipboardData || window.clipboardData);
31381 // check what type of paste - if it's an image, then handle it differently.
31382 if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31384 var urlAPI = (window.createObjectURL && window) ||
31385 (window.URL && URL.revokeObjectURL && URL) ||
31386 (window.webkitURL && webkitURL);
31388 var r = new FileReader();
31390 r.addEventListener('load',function()
31393 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31394 // is insert asycn?
31395 if (t.enableBlocks) {
31397 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31398 if (img.closest('figure')) { // assume!! that it's aready
31401 var fig = new Roo.htmleditor.BlockFigure({
31402 image_src : img.src
31404 fig.updateElement(img); // replace it..
31408 t.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31409 t.owner.fireEvent('paste', this);
31411 r.readAsDataURL(cd.files[0]);
31413 e.preventDefault();
31417 if (cd.types.indexOf('text/html') < 0 ) {
31421 var html = cd.getData('text/html'); // clipboard event
31422 if (cd.types.indexOf('text/rtf') > -1) {
31423 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31424 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31429 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31430 .map(function(g) { return g.toDataURL(); })
31431 .filter(function(g) { return g != 'about:blank'; });
31434 html = this.cleanWordChars(html);
31436 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31439 var sn = this.getParentElement();
31440 // check if d contains a table, and prevent nesting??
31441 //Roo.log(d.getElementsByTagName('table'));
31443 //Roo.log(sn.closest('table'));
31444 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31445 e.preventDefault();
31446 this.insertAtCursor("You can not nest tables");
31447 //Roo.log("prevent?"); // fixme -
31453 if (images.length > 0) {
31454 // replace all v:imagedata - with img.
31455 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31456 Roo.each(ar, function(node) {
31457 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31458 node.parentNode.removeChild(node);
31462 Roo.each(d.getElementsByTagName('img'), function(img, i) {
31463 img.setAttribute('src', images[i]);
31466 if (this.autoClean) {
31467 new Roo.htmleditor.FilterWord({ node : d });
31469 new Roo.htmleditor.FilterStyleToTag({ node : d });
31470 new Roo.htmleditor.FilterAttributes({
31472 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31473 attrib_clean : ['href', 'src' ]
31475 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31476 // should be fonts..
31477 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31478 new Roo.htmleditor.FilterParagraph({ node : d });
31479 new Roo.htmleditor.FilterSpan({ node : d });
31480 new Roo.htmleditor.FilterLongBr({ node : d });
31481 new Roo.htmleditor.FilterComment({ node : d });
31485 if (this.enableBlocks) {
31487 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31488 if (img.closest('figure')) { // assume!! that it's aready
31491 var fig = new Roo.htmleditor.BlockFigure({
31492 image_src : img.src
31494 fig.updateElement(img); // replace it..
31500 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31501 if (this.enableBlocks) {
31502 Roo.htmleditor.Block.initAll(this.doc.body);
31506 e.preventDefault();
31507 this.owner.fireEvent('paste', this);
31509 // default behaveiour should be our local cleanup paste? (optional?)
31510 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31511 //this.owner.fireEvent('paste', e, v);
31514 onDestroy : function(){
31520 //for (var i =0; i < this.toolbars.length;i++) {
31521 // // fixme - ask toolbars for heights?
31522 // this.toolbars[i].onDestroy();
31525 //this.wrap.dom.innerHTML = '';
31526 //this.wrap.remove();
31531 onFirstFocus : function(){
31533 this.assignDocWin();
31534 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31536 this.activated = true;
31539 if(Roo.isGecko){ // prevent silly gecko errors
31541 var s = this.win.getSelection();
31542 if(!s.focusNode || s.focusNode.nodeType != 3){
31543 var r = s.getRangeAt(0);
31544 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31549 this.execCmd('useCSS', true);
31550 this.execCmd('styleWithCSS', false);
31553 this.owner.fireEvent('activate', this);
31557 adjustFont: function(btn){
31558 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31559 //if(Roo.isSafari){ // safari
31562 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31563 if(Roo.isSafari){ // safari
31564 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31565 v = (v < 10) ? 10 : v;
31566 v = (v > 48) ? 48 : v;
31567 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31572 v = Math.max(1, v+adjust);
31574 this.execCmd('FontSize', v );
31577 onEditorEvent : function(e)
31581 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31582 return; // we do not handle this.. (undo manager does..)
31584 // clicking a 'block'?
31586 // in theory this detects if the last element is not a br, then we try and do that.
31587 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31589 e.target.nodeName == 'BODY' &&
31590 e.type == "mouseup" &&
31591 this.doc.body.lastChild
31593 var lc = this.doc.body.lastChild;
31594 // gtx-trans is google translate plugin adding crap.
31595 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31596 lc = lc.previousSibling;
31598 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31599 // if last element is <BR> - then dont do anything.
31601 var ns = this.doc.createElement('br');
31602 this.doc.body.appendChild(ns);
31603 range = this.doc.createRange();
31604 range.setStartAfter(ns);
31605 range.collapse(true);
31606 var sel = this.win.getSelection();
31607 sel.removeAllRanges();
31608 sel.addRange(range);
31614 this.fireEditorEvent(e);
31615 // this.updateToolbar();
31616 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31619 fireEditorEvent: function(e)
31621 this.owner.fireEvent('editorevent', this, e);
31624 insertTag : function(tg)
31626 // could be a bit smarter... -> wrap the current selected tRoo..
31627 if (tg.toLowerCase() == 'span' ||
31628 tg.toLowerCase() == 'code' ||
31629 tg.toLowerCase() == 'sup' ||
31630 tg.toLowerCase() == 'sub'
31633 range = this.createRange(this.getSelection());
31634 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31635 wrappingNode.appendChild(range.extractContents());
31636 range.insertNode(wrappingNode);
31643 this.execCmd("formatblock", tg);
31644 this.undoManager.addEvent();
31647 insertText : function(txt)
31651 var range = this.createRange();
31652 range.deleteContents();
31653 //alert(Sender.getAttribute('label'));
31655 range.insertNode(this.doc.createTextNode(txt));
31656 this.undoManager.addEvent();
31662 * Executes a Midas editor command on the editor document and performs necessary focus and
31663 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31664 * @param {String} cmd The Midas command
31665 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31667 relayCmd : function(cmd, value)
31671 case 'justifyleft':
31672 case 'justifyright':
31673 case 'justifycenter':
31674 // if we are in a cell, then we will adjust the
31675 var n = this.getParentElement();
31676 var td = n.closest('td');
31678 var bl = Roo.htmleditor.Block.factory(td);
31679 bl.textAlign = cmd.replace('justify','');
31680 bl.updateElement();
31681 this.owner.fireEvent('editorevent', this);
31684 this.execCmd('styleWithCSS', true); //
31689 // if there is no selection, then we insert, and set the curson inside it..
31690 this.execCmd('styleWithCSS', false);
31700 this.execCmd(cmd, value);
31701 this.owner.fireEvent('editorevent', this);
31702 //this.updateToolbar();
31703 this.owner.deferFocus();
31707 * Executes a Midas editor command directly on the editor document.
31708 * For visual commands, you should use {@link #relayCmd} instead.
31709 * <b>This should only be called after the editor is initialized.</b>
31710 * @param {String} cmd The Midas command
31711 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31713 execCmd : function(cmd, value){
31714 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31721 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31723 * @param {String} text | dom node..
31725 insertAtCursor : function(text)
31728 if(!this.activated){
31732 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31736 // from jquery ui (MIT licenced)
31738 var win = this.win;
31740 if (win.getSelection && win.getSelection().getRangeAt) {
31742 // delete the existing?
31744 this.createRange(this.getSelection()).deleteContents();
31745 range = win.getSelection().getRangeAt(0);
31746 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31747 range.insertNode(node);
31748 range = range.cloneRange();
31749 range.collapse(false);
31751 win.getSelection().removeAllRanges();
31752 win.getSelection().addRange(range);
31756 } else if (win.document.selection && win.document.selection.createRange) {
31757 // no firefox support
31758 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31759 win.document.selection.createRange().pasteHTML(txt);
31762 // no firefox support
31763 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31764 this.execCmd('InsertHTML', txt);
31772 mozKeyPress : function(e){
31774 var c = e.getCharCode(), cmd;
31777 c = String.fromCharCode(c).toLowerCase();
31791 // this.cleanUpPaste.defer(100, this);
31797 this.relayCmd(cmd);
31798 //this.win.focus();
31799 //this.execCmd(cmd);
31800 //this.deferFocus();
31801 e.preventDefault();
31809 fixKeys : function(){ // load time branching for fastest keydown performance
31813 return function(e){
31814 var k = e.getKey(), r;
31817 r = this.doc.selection.createRange();
31820 r.pasteHTML('    ');
31825 /// this is handled by Roo.htmleditor.KeyEnter
31828 r = this.doc.selection.createRange();
31830 var target = r.parentElement();
31831 if(!target || target.tagName.toLowerCase() != 'li'){
31833 r.pasteHTML('<br/>');
31840 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31841 // this.cleanUpPaste.defer(100, this);
31847 }else if(Roo.isOpera){
31848 return function(e){
31849 var k = e.getKey();
31853 this.execCmd('InsertHTML','    ');
31857 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31858 // this.cleanUpPaste.defer(100, this);
31863 }else if(Roo.isSafari){
31864 return function(e){
31865 var k = e.getKey();
31869 this.execCmd('InsertText','\t');
31873 this.mozKeyPress(e);
31875 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31876 // this.cleanUpPaste.defer(100, this);
31884 getAllAncestors: function()
31886 var p = this.getSelectedNode();
31889 a.push(p); // push blank onto stack..
31890 p = this.getParentElement();
31894 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31898 a.push(this.doc.body);
31902 lastSelNode : false,
31905 getSelection : function()
31907 this.assignDocWin();
31908 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31911 * Select a dom node
31912 * @param {DomElement} node the node to select
31914 selectNode : function(node, collapse)
31916 var nodeRange = node.ownerDocument.createRange();
31918 nodeRange.selectNode(node);
31920 nodeRange.selectNodeContents(node);
31922 if (collapse === true) {
31923 nodeRange.collapse(true);
31926 var s = this.win.getSelection();
31927 s.removeAllRanges();
31928 s.addRange(nodeRange);
31931 getSelectedNode: function()
31933 // this may only work on Gecko!!!
31935 // should we cache this!!!!
31939 var range = this.createRange(this.getSelection()).cloneRange();
31942 var parent = range.parentElement();
31944 var testRange = range.duplicate();
31945 testRange.moveToElementText(parent);
31946 if (testRange.inRange(range)) {
31949 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31952 parent = parent.parentElement;
31957 // is ancestor a text element.
31958 var ac = range.commonAncestorContainer;
31959 if (ac.nodeType == 3) {
31960 ac = ac.parentNode;
31963 var ar = ac.childNodes;
31966 var other_nodes = [];
31967 var has_other_nodes = false;
31968 for (var i=0;i<ar.length;i++) {
31969 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31972 // fullly contained node.
31974 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31979 // probably selected..
31980 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31981 other_nodes.push(ar[i]);
31985 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
31990 has_other_nodes = true;
31992 if (!nodes.length && other_nodes.length) {
31993 nodes= other_nodes;
31995 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32003 createRange: function(sel)
32005 // this has strange effects when using with
32006 // top toolbar - not sure if it's a great idea.
32007 //this.editor.contentWindow.focus();
32008 if (typeof sel != "undefined") {
32010 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32012 return this.doc.createRange();
32015 return this.doc.createRange();
32018 getParentElement: function()
32021 this.assignDocWin();
32022 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32024 var range = this.createRange(sel);
32027 var p = range.commonAncestorContainer;
32028 while (p.nodeType == 3) { // text node
32039 * Range intersection.. the hard stuff...
32043 * [ -- selected range --- ]
32047 * if end is before start or hits it. fail.
32048 * if start is after end or hits it fail.
32050 * if either hits (but other is outside. - then it's not
32056 // @see http://www.thismuchiknow.co.uk/?p=64.
32057 rangeIntersectsNode : function(range, node)
32059 var nodeRange = node.ownerDocument.createRange();
32061 nodeRange.selectNode(node);
32063 nodeRange.selectNodeContents(node);
32066 var rangeStartRange = range.cloneRange();
32067 rangeStartRange.collapse(true);
32069 var rangeEndRange = range.cloneRange();
32070 rangeEndRange.collapse(false);
32072 var nodeStartRange = nodeRange.cloneRange();
32073 nodeStartRange.collapse(true);
32075 var nodeEndRange = nodeRange.cloneRange();
32076 nodeEndRange.collapse(false);
32078 return rangeStartRange.compareBoundaryPoints(
32079 Range.START_TO_START, nodeEndRange) == -1 &&
32080 rangeEndRange.compareBoundaryPoints(
32081 Range.START_TO_START, nodeStartRange) == 1;
32085 rangeCompareNode : function(range, node)
32087 var nodeRange = node.ownerDocument.createRange();
32089 nodeRange.selectNode(node);
32091 nodeRange.selectNodeContents(node);
32095 range.collapse(true);
32097 nodeRange.collapse(true);
32099 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32100 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
32102 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32104 var nodeIsBefore = ss == 1;
32105 var nodeIsAfter = ee == -1;
32107 if (nodeIsBefore && nodeIsAfter) {
32110 if (!nodeIsBefore && nodeIsAfter) {
32111 return 1; //right trailed.
32114 if (nodeIsBefore && !nodeIsAfter) {
32115 return 2; // left trailed.
32121 cleanWordChars : function(input) {// change the chars to hex code
32124 [ 8211, "–" ],
32125 [ 8212, "—" ],
32133 var output = input;
32134 Roo.each(swapCodes, function(sw) {
32135 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32137 output = output.replace(swapper, sw[1]);
32147 cleanUpChild : function (node)
32150 new Roo.htmleditor.FilterComment({node : node});
32151 new Roo.htmleditor.FilterAttributes({
32153 attrib_black : this.ablack,
32154 attrib_clean : this.aclean,
32155 style_white : this.cwhite,
32156 style_black : this.cblack
32158 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32159 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32165 * Clean up MS wordisms...
32166 * @deprecated - use filter directly
32168 cleanWord : function(node)
32170 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32171 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32178 * @deprecated - use filters
32180 cleanTableWidths : function(node)
32182 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32189 applyBlacklists : function()
32191 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
32192 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
32194 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
32195 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
32196 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
32200 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32201 if (b.indexOf(tag) > -1) {
32204 this.white.push(tag);
32208 Roo.each(w, function(tag) {
32209 if (b.indexOf(tag) > -1) {
32212 if (this.white.indexOf(tag) > -1) {
32215 this.white.push(tag);
32220 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32221 if (w.indexOf(tag) > -1) {
32224 this.black.push(tag);
32228 Roo.each(b, function(tag) {
32229 if (w.indexOf(tag) > -1) {
32232 if (this.black.indexOf(tag) > -1) {
32235 this.black.push(tag);
32240 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
32241 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
32245 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32246 if (b.indexOf(tag) > -1) {
32249 this.cwhite.push(tag);
32253 Roo.each(w, function(tag) {
32254 if (b.indexOf(tag) > -1) {
32257 if (this.cwhite.indexOf(tag) > -1) {
32260 this.cwhite.push(tag);
32265 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32266 if (w.indexOf(tag) > -1) {
32269 this.cblack.push(tag);
32273 Roo.each(b, function(tag) {
32274 if (w.indexOf(tag) > -1) {
32277 if (this.cblack.indexOf(tag) > -1) {
32280 this.cblack.push(tag);
32285 setStylesheets : function(stylesheets)
32287 if(typeof(stylesheets) == 'string'){
32288 Roo.get(this.iframe.contentDocument.head).createChild({
32290 rel : 'stylesheet',
32299 Roo.each(stylesheets, function(s) {
32304 Roo.get(_this.iframe.contentDocument.head).createChild({
32306 rel : 'stylesheet',
32316 updateLanguage : function()
32318 if (!this.iframe || !this.iframe.contentDocument) {
32321 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32325 removeStylesheets : function()
32329 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32334 setStyle : function(style)
32336 Roo.get(this.iframe.contentDocument.head).createChild({
32345 // hide stuff that is not compatible
32359 * @event specialkey
32363 * @cfg {String} fieldClass @hide
32366 * @cfg {String} focusClass @hide
32369 * @cfg {String} autoCreate @hide
32372 * @cfg {String} inputType @hide
32375 * @cfg {String} invalidClass @hide
32378 * @cfg {String} invalidText @hide
32381 * @cfg {String} msgFx @hide
32384 * @cfg {String} validateOnBlur @hide
32388 Roo.HtmlEditorCore.white = [
32389 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32391 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
32392 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
32393 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
32394 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
32395 'TABLE', 'UL', 'XMP',
32397 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
32400 'DIR', 'MENU', 'OL', 'UL', 'DL',
32406 Roo.HtmlEditorCore.black = [
32407 // 'embed', 'object', // enable - backend responsiblity to clean thiese
32409 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
32410 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
32411 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
32412 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
32413 //'FONT' // CLEAN LATER..
32414 'COLGROUP', 'COL' // messy tables.
32418 Roo.HtmlEditorCore.clean = [ // ?? needed???
32419 'SCRIPT', 'STYLE', 'TITLE', 'XML'
32421 Roo.HtmlEditorCore.tag_remove = [
32426 Roo.HtmlEditorCore.ablack = [
32430 Roo.HtmlEditorCore.aclean = [
32431 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
32435 Roo.HtmlEditorCore.pwhite= [
32436 'http', 'https', 'mailto'
32439 // white listed style attributes.
32440 Roo.HtmlEditorCore.cwhite= [
32441 // 'text-align', /// default is to allow most things..
32447 // black listed style attributes.
32448 Roo.HtmlEditorCore.cblack= [
32449 // 'font-size' -- this can be set by the project
32463 * @class Roo.bootstrap.form.HtmlEditor
32464 * @extends Roo.bootstrap.form.TextArea
32465 * Bootstrap HtmlEditor class
32468 * Create a new HtmlEditor
32469 * @param {Object} config The config object
32472 Roo.bootstrap.form.HtmlEditor = function(config){
32476 * @event initialize
32477 * Fires when the editor is fully initialized (including the iframe)
32478 * @param {Roo.bootstrap.form.HtmlEditor} this
32483 * Fires when the editor is first receives the focus. Any insertion must wait
32484 * until after this event.
32485 * @param {Roo.bootstrap.form.HtmlEditor} this
32489 * @event beforesync
32490 * Fires before the textarea is updated with content from the editor iframe. Return false
32491 * to cancel the sync.
32492 * @param {Roo.bootstrap.form.HtmlEditor} this
32493 * @param {String} html
32497 * @event beforepush
32498 * Fires before the iframe editor is updated with content from the textarea. Return false
32499 * to cancel the push.
32500 * @param {Roo.bootstrap.form.HtmlEditor} this
32501 * @param {String} html
32506 * Fires when the textarea is updated with content from the editor iframe.
32507 * @param {Roo.bootstrap.form.HtmlEditor} this
32508 * @param {String} html
32513 * Fires when the iframe editor is updated with content from the textarea.
32514 * @param {Roo.bootstrap.form.HtmlEditor} this
32515 * @param {String} html
32519 * @event editmodechange
32520 * Fires when the editor switches edit modes
32521 * @param {Roo.bootstrap.form.HtmlEditor} this
32522 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32524 editmodechange: true,
32526 * @event editorevent
32527 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32528 * @param {Roo.bootstrap.form.HtmlEditor} this
32532 * @event firstfocus
32533 * Fires when on first focus - needed by toolbars..
32534 * @param {Roo.bootstrap.form.HtmlEditor} this
32539 * Auto save the htmlEditor value as a file into Events
32540 * @param {Roo.bootstrap.form.HtmlEditor} this
32544 * @event savedpreview
32545 * preview the saved version of htmlEditor
32546 * @param {Roo.bootstrap.form.HtmlEditor} this
32548 savedpreview: true,
32550 * @event stylesheetsclick
32551 * Fires when press the Sytlesheets button
32552 * @param {Roo.HtmlEditorCore} this
32554 stylesheetsclick: true,
32557 * Fires when press user pastes into the editor
32558 * @param {Roo.HtmlEditorCore} this
32563 * Fires when on any editor when an image is added (excluding paste)
32564 * @param {Roo.bootstrap.form.HtmlEditor} this
32568 * @event imageupdated
32569 * Fires when on any editor when an image is changed (excluding paste)
32570 * @param {Roo.bootstrap.form.HtmlEditor} this
32571 * @param {HTMLElement} img could also be a figure if blocks are enabled
32573 imageupdate: true ,
32575 * @event imagedelete
32576 * Fires when on any editor when an image is deleted
32577 * @param {Roo.bootstrap.form.HtmlEditor} this
32578 * @param {HTMLElement} img could also be a figure if blocks are enabled
32582 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32583 if (!this.toolbars) {
32584 this.toolbars = [];
32587 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32592 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
32596 * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32601 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32606 * @cfg {String} resize (none|both|horizontal|vertical) - css resize of element
32610 * @cfg {Number} height (in pixels)
32614 * @cfg {Number} width (in pixels)
32619 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32622 stylesheets: false,
32627 // private properties
32628 validationEvent : false,
32630 initialized : false,
32633 onFocus : Roo.emptyFn,
32635 hideMode:'offsets',
32637 tbContainer : false,
32641 toolbarContainer :function() {
32642 return this.wrap.select('.x-html-editor-tb',true).first();
32646 * Protected method that will not generally be called directly. It
32647 * is called when the editor creates its toolbar. Override this method if you need to
32648 * add custom toolbar buttons.
32649 * @param {HtmlEditor} editor
32651 createToolbar : function()
32653 //Roo.log('renewing');
32654 //Roo.log("create toolbars");
32655 if (this.toolbars === false) {
32658 if (this.toolbars === true) {
32659 this.toolbars = [ 'Standard' ];
32662 var ar = Array.from(this.toolbars);
32663 this.toolbars = [];
32664 ar.forEach(function(t,i) {
32665 if (typeof(t) == 'string') {
32670 if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32672 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32673 t = Roo.factory(t);
32675 this.toolbars[i] = t;
32676 this.toolbars[i].render(this.toolbarContainer());
32684 onRender : function(ct, position)
32686 // Roo.log("Call onRender: " + this.xtype);
32688 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32690 this.wrap = this.inputEl().wrap({
32691 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32694 this.editorcore.onRender(ct, position);
32697 this.createToolbar(this);
32705 onResize : function(w, h)
32707 Roo.log('resize: ' +w + ',' + h );
32708 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32712 if(this.inputEl() ){
32713 if(typeof w == 'number'){
32714 var aw = w - this.wrap.getFrameWidth('lr');
32715 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32718 if(typeof h == 'number'){
32719 var tbh = -11; // fixme it needs to tool bar size!
32720 for (var i =0; i < this.toolbars.length;i++) {
32721 // fixme - ask toolbars for heights?
32722 tbh += this.toolbars[i].el.getHeight();
32723 //if (this.toolbars[i].footer) {
32724 // tbh += this.toolbars[i].footer.el.getHeight();
32732 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32733 ah -= 5; // knock a few pixes off for look..
32734 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32738 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32739 this.editorcore.onResize(ew,eh);
32744 * Toggles the editor between standard and source edit mode.
32745 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32747 toggleSourceEdit : function(sourceEditMode)
32749 this.editorcore.toggleSourceEdit(sourceEditMode);
32751 if(this.editorcore.sourceEditMode){
32752 Roo.log('editor - showing textarea');
32755 // Roo.log(this.syncValue());
32757 this.inputEl().removeClass(['hide', 'x-hidden']);
32758 this.inputEl().dom.removeAttribute('tabIndex');
32759 this.inputEl().focus();
32761 Roo.log('editor - hiding textarea');
32763 // Roo.log(this.pushValue());
32766 this.inputEl().addClass(['hide', 'x-hidden']);
32767 this.inputEl().dom.setAttribute('tabIndex', -1);
32768 //this.deferFocus();
32771 //if(this.resizable){
32772 // this.setSize(this.wrap.getSize());
32775 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32778 // private (for BoxComponent)
32779 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32781 // private (for BoxComponent)
32782 getResizeEl : function(){
32786 // private (for BoxComponent)
32787 getPositionEl : function(){
32792 initEvents : function(){
32793 this.originalValue = this.getValue();
32797 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32800 // markInvalid : Roo.emptyFn,
32802 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32805 // clearInvalid : Roo.emptyFn,
32807 setValue : function(v){
32808 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32809 this.editorcore.pushValue();
32814 deferFocus : function(){
32815 this.focus.defer(10, this);
32819 focus : function(){
32820 this.editorcore.focus();
32826 onDestroy : function(){
32832 for (var i =0; i < this.toolbars.length;i++) {
32833 // fixme - ask toolbars for heights?
32834 this.toolbars[i].onDestroy();
32837 this.wrap.dom.innerHTML = '';
32838 this.wrap.remove();
32843 onFirstFocus : function(){
32844 //Roo.log("onFirstFocus");
32845 this.editorcore.onFirstFocus();
32846 for (var i =0; i < this.toolbars.length;i++) {
32847 this.toolbars[i].onFirstFocus();
32853 syncValue : function()
32855 this.editorcore.syncValue();
32858 pushValue : function()
32860 this.editorcore.pushValue();
32864 // hide stuff that is not compatible
32878 * @event specialkey
32882 * @cfg {String} fieldClass @hide
32885 * @cfg {String} focusClass @hide
32888 * @cfg {String} autoCreate @hide
32891 * @cfg {String} inputType @hide
32895 * @cfg {String} invalidText @hide
32898 * @cfg {String} msgFx @hide
32901 * @cfg {String} validateOnBlur @hide
32911 * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32912 * @parent Roo.bootstrap.form.HtmlEditor
32913 * @extends Roo.bootstrap.nav.Simplebar
32919 new Roo.bootstrap.form.HtmlEditor({
32922 new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32923 disable : { fonts: 1 , format: 1, ..., ... , ...],
32929 * @cfg {Object} disable List of elements to disable..
32930 * @cfg {Array} btns List of additional buttons.
32934 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32937 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32940 Roo.apply(this, config);
32942 // default disabled, based on 'good practice'..
32943 this.disable = this.disable || {};
32944 Roo.applyIf(this.disable, {
32947 specialElements : true
32949 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32951 this.editor = config.editor;
32952 this.editorcore = config.editor.editorcore;
32954 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32956 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32957 // dont call parent... till later.
32959 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar, {
32964 editorcore : false,
32969 "h1","h2","h3","h4","h5","h6",
32971 "abbr", "acronym", "address", "cite", "samp", "var",
32978 onRender : function(ct, position)
32980 // Roo.log("Call onRender: " + this.xtype);
32982 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
32984 this.el.dom.style.marginBottom = '0';
32986 var editorcore = this.editorcore;
32987 var editor= this.editor;
32990 var btn = function(id, cmd , toggle, handler, html){
32992 var event = toggle ? 'toggle' : 'click';
32997 xns: Roo.bootstrap,
33001 cls : 'roo-html-editor-btn-' + id,
33002 cmd : cmd, // why id || cmd
33003 enableToggle: toggle !== false,
33005 pressed : toggle ? false : null,
33008 a.listeners[toggle ? 'toggle' : 'click'] = function() {
33009 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
33015 // var cb_box = function...
33020 xns: Roo.bootstrap,
33022 cls : 'roo-html-editor-font-chooser',
33026 xns: Roo.bootstrap,
33030 Roo.each(this.formats, function(f) {
33031 style.menu.items.push({
33033 xns: Roo.bootstrap,
33034 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33039 editorcore.insertTag(this.tagname);
33046 children.push(style);
33048 btn('bold', 'bold',true);
33049 btn('italic', 'italic',true);
33050 btn('underline', 'underline',true);
33051 btn('align-left', 'justifyleft',true);
33052 btn('align-center', 'justifycenter',true);
33053 btn('align-right' , 'justifyright',true);
33054 btn('link', false, true, this.onLinkClick);
33057 btn('image', false, true, this.onImageClick);
33058 btn('list','insertunorderedlist',true);
33059 btn('list-ol','insertorderedlist',true);
33061 btn('pencil', false,true, function(btn){
33063 this.toggleSourceEdit(btn.pressed);
33066 if (this.editor.btns.length > 0) {
33067 for (var i = 0; i<this.editor.btns.length; i++) {
33068 children.push(this.editor.btns[i]);
33074 this.xtype = 'NavSimplebar'; // why?
33076 for(var i=0;i< children.length;i++) {
33078 this.buttons.add(this.addxtypeChild(children[i]));
33081 this.buildToolbarDelete();
33083 editor.on('editorevent', this.updateToolbar, this);
33086 buildToolbarDelete : function()
33089 /* this.addxtypeChild({
33091 xns : Roo.bootstrap,
33092 cls : 'roo-htmleditor-fill'
33095 this.deleteBtn = this.addxtypeChild({
33098 xns: Roo.bootstrap,
33101 click : this.onDelete.createDelegate(this)
33104 this.deleteBtn.hide();
33108 onImageClick : function()
33111 this.input.un('change', this.onFileSelected, this);
33113 this.input = Roo.get(document.body).createChild({
33116 style : 'display:none',
33117 multiple: 'multiple'
33119 this.input.on('change', this.onFileSelected, this);
33120 this.input.dom.click();
33123 onFileSelected : function(e)
33125 e.preventDefault();
33127 if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33132 this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33135 addFiles : function(far, fire_add) {
33138 var editor = this.editorcore;
33142 this.editor.syncValue();
33143 editor.owner.fireEvent('editorevent', editor.owner, false);
33144 editor.owner.fireEvent('imageadd', editor.owner, false);
33151 if (!f.type.match(/^image/)) {
33152 this.addFiles(far, fire_add);
33156 var sn = this.selectedNode;
33158 var bl = sn && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33161 var reader = new FileReader();
33162 reader.addEventListener('load', (function() {
33164 bl.image_src = reader.result;
33165 //bl.caption = f.name;
33166 bl.updateElement(sn);
33167 this.editor.syncValue();
33168 editor.owner.fireEvent('editorevent', editor.owner, false);
33169 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33170 // we only do the first file!! and replace.
33173 if (this.editorcore.enableBlocks) {
33174 var fig = new Roo.htmleditor.BlockFigure({
33175 image_src : reader.result,
33177 caption_display : 'none' //default to hide captions..
33179 editor.insertAtCursor(fig.toHTML());
33180 this.addFiles(far, true);
33183 // just a standard img..
33184 if (sn && sn.tagName.toUpperCase() == 'IMG') {
33185 sn.src = reader.result;
33186 this.editor.syncValue();
33187 editor.owner.fireEvent('editorevent', editor.owner, false);
33188 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33191 editor.insertAtCursor('<img src="' + reader.result +'">');
33192 this.addFiles(far, true);
33194 }).createDelegate(this));
33195 reader.readAsDataURL(f);
33201 onBtnClick : function(id)
33203 this.editorcore.relayCmd(id);
33204 this.editorcore.focus();
33207 onLinkClick : function(btn) {
33208 var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33209 this.selectedNode.getAttribute('href') : '';
33211 Roo.bootstrap.MessageBox.show({
33212 title : "Add / Edit Link URL",
33213 msg : "Enter the URL for the link",
33214 buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33221 fn: function(pressed, newurl) {
33222 if (pressed != 'ok') {
33223 this.editorcore.focus();
33227 this.selectedNode.setAttribute('href', newurl);
33230 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33231 this.editorcore.relayCmd('createlink', newurl);
33233 this.editorcore.focus();
33238 * Protected method that will not generally be called directly. It triggers
33239 * a toolbar update by reading the markup state of the current selection in the editor.
33241 updateToolbar: function(editor ,ev, sel){
33243 if(!this.editorcore.activated){
33244 this.editor.onFirstFocus(); // is this neeed?
33248 var btns = this.buttons;
33249 var doc = this.editorcore.doc;
33250 var hasToggle = false;
33251 btns.each(function(e) {
33252 if (e.enableToggle && e.cmd) {
33253 hasToggle = hasToggle || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33254 e.setActive(doc.queryCommandState(e.cmd));
33260 (ev.type == 'mouseup' || ev.type == 'click' ) &&
33261 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33262 // they have click on an image...
33263 // let's see if we can change the selection...
33268 var ans = this.editorcore.getAllAncestors();
33270 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
33271 sel = sel ? sel : this.editorcore.doc.body;
33272 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33276 var lastSel = this.selectedNode;
33277 this.selectedNode = sel;
33279 // ok see if we are editing a block?
33282 // you are not actually selecting the block.
33283 if (sel && sel.hasAttribute('data-block')) {
33285 } else if (sel && sel.closest('[data-block]')) {
33286 db = sel.closest('[data-block]');
33289 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33290 e.classList.remove('roo-ed-selection');
33294 if (db && this.editorcore.enableBlocks) {
33295 block = Roo.htmleditor.Block.factory(db);
33298 db.className = (db.classList.length > 0 ? db.className + ' ' : '') +
33299 ' roo-ed-selection';
33300 sel = this.selectedNode = db;
33304 // highlight the 'a'..
33305 var tn = sel && sel.tagName.toUpperCase() || '';
33306 if (!block && sel && tn != 'A') {
33307 var asel = sel.closest('A');
33313 btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33314 btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33315 btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33317 Roo.bootstrap.menu.Manager.hideAll();
33323 // handle delete button..
33324 if (hasToggle || (tn.length && tn == 'BODY')) {
33325 this.deleteBtn.hide();
33329 this.deleteBtn.show();
33333 //this.editorsyncValue();
33335 onFirstFocus: function() {
33336 this.buttons.each(function(item){
33341 onDelete : function()
33343 var range = this.editorcore.createRange();
33344 var selection = this.editorcore.getSelection();
33345 var sn = this.selectedNode;
33346 range.setStart(sn,0);
33347 range.setEnd(sn,0);
33350 if (sn.hasAttribute('data-block')) {
33351 var block = Roo.htmleditor.Block.factory(this.selectedNode);
33353 sn = block.removeNode();
33354 sn.parentNode.removeChild(sn);
33355 selection.removeAllRanges();
33356 selection.addRange(range);
33357 this.updateToolbar(null, null, null);
33358 if (sn.tagName.toUpperCase() == 'FIGURE') {
33359 this.editor.syncValue();
33360 this.editor.fireEvent('imagedelete', this.editor, sn);
33363 this.selectedNode = false;
33364 this.editorcore.fireEditorEvent(false);
33370 return; // should not really happen..
33372 if (sn && sn.tagName == 'BODY') {
33375 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33377 // remove and keep parents.
33378 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33381 selection.removeAllRanges();
33382 selection.addRange(range);
33383 if (sn.tagName.toUpperCase() == 'IMG"') {
33384 this.editor.syncValue();
33385 this.editor.fireEvent('imagedelete', this.editor, sn);
33388 this.selectedNode = false;
33389 this.editorcore.fireEditorEvent(false);
33395 toggleSourceEdit : function(sourceEditMode){
33398 if(sourceEditMode){
33399 Roo.log("disabling buttons");
33400 this.buttons.each( function(item){
33401 if(item.cmd != 'pencil'){
33407 Roo.log("enabling buttons");
33408 if(this.editorcore.initialized){
33409 this.buttons.each( function(item){
33415 Roo.log("calling toggole on editor");
33416 // tell the editor that it's been pressed..
33417 this.editor.toggleSourceEdit(sourceEditMode);
33431 * @class Roo.bootstrap.form.Markdown
33432 * @extends Roo.bootstrap.form.TextArea
33433 * Bootstrap Showdown editable area
33434 * @cfg {string} content
33437 * Create a new Showdown
33440 Roo.bootstrap.form.Markdown = function(config){
33441 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33445 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
33449 initEvents : function()
33452 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33453 this.markdownEl = this.el.createChild({
33454 cls : 'roo-markdown-area'
33456 this.inputEl().addClass('d-none');
33457 if (this.getValue() == '') {
33458 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33461 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33463 this.markdownEl.on('click', this.toggleTextEdit, this);
33464 this.on('blur', this.toggleTextEdit, this);
33465 this.on('specialkey', this.resizeTextArea, this);
33468 toggleTextEdit : function()
33470 var sh = this.markdownEl.getHeight();
33471 this.inputEl().addClass('d-none');
33472 this.markdownEl.addClass('d-none');
33473 if (!this.editing) {
33475 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33476 this.inputEl().removeClass('d-none');
33477 this.inputEl().focus();
33478 this.editing = true;
33481 // show showdown...
33482 this.updateMarkdown();
33483 this.markdownEl.removeClass('d-none');
33484 this.editing = false;
33487 updateMarkdown : function()
33489 if (this.getValue() == '') {
33490 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33494 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33497 resizeTextArea: function () {
33500 Roo.log([sh, this.getValue().split("\n").length * 30]);
33501 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33503 setValue : function(val)
33505 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33506 if (!this.editing) {
33507 this.updateMarkdown();
33513 if (!this.editing) {
33514 this.toggleTextEdit();
33522 * Ext JS Library 1.1.1
33523 * Copyright(c) 2006-2007, Ext JS, LLC.
33525 * Originally Released Under LGPL - original licence link has changed is not relivant.
33528 * <script type="text/javascript">
33532 * @class Roo.bootstrap.PagingToolbar
33533 * @extends Roo.bootstrap.nav.Simplebar
33534 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33536 * Create a new PagingToolbar
33537 * @param {Object} config The config object
33538 * @param {Roo.data.Store} store
33540 Roo.bootstrap.PagingToolbar = function(config)
33542 // old args format still supported... - xtype is prefered..
33543 // created from xtype...
33545 this.ds = config.dataSource;
33547 if (config.store && !this.ds) {
33548 this.store= Roo.factory(config.store, Roo.data);
33549 this.ds = this.store;
33550 this.ds.xmodule = this.xmodule || false;
33553 this.toolbarItems = [];
33554 if (config.items) {
33555 this.toolbarItems = config.items;
33558 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33563 this.bind(this.ds);
33566 if (Roo.bootstrap.version == 4) {
33567 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33569 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33574 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33576 * @cfg {Roo.bootstrap.Button} buttons[]
33577 * Buttons for the toolbar
33580 * @cfg {Roo.data.Store} store
33581 * The underlying data store providing the paged data
33584 * @cfg {String/HTMLElement/Element} container
33585 * container The id or element that will contain the toolbar
33588 * @cfg {Boolean} displayInfo
33589 * True to display the displayMsg (defaults to false)
33592 * @cfg {Number} pageSize
33593 * The number of records to display per page (defaults to 20)
33597 * @cfg {String} displayMsg
33598 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33600 displayMsg : 'Displaying {0} - {1} of {2}',
33602 * @cfg {String} emptyMsg
33603 * The message to display when no records are found (defaults to "No data to display")
33605 emptyMsg : 'No data to display',
33607 * Customizable piece of the default paging text (defaults to "Page")
33610 beforePageText : "Page",
33612 * Customizable piece of the default paging text (defaults to "of %0")
33615 afterPageText : "of {0}",
33617 * Customizable piece of the default paging text (defaults to "First Page")
33620 firstText : "First Page",
33622 * Customizable piece of the default paging text (defaults to "Previous Page")
33625 prevText : "Previous Page",
33627 * Customizable piece of the default paging text (defaults to "Next Page")
33630 nextText : "Next Page",
33632 * Customizable piece of the default paging text (defaults to "Last Page")
33635 lastText : "Last Page",
33637 * Customizable piece of the default paging text (defaults to "Refresh")
33640 refreshText : "Refresh",
33644 onRender : function(ct, position)
33646 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33647 this.navgroup.parentId = this.id;
33648 this.navgroup.onRender(this.el, null);
33649 // add the buttons to the navgroup
33651 if(this.displayInfo){
33652 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33653 this.displayEl = this.el.select('.x-paging-info', true).first();
33654 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33655 // this.displayEl = navel.el.select('span',true).first();
33661 Roo.each(_this.buttons, function(e){ // this might need to use render????
33662 Roo.factory(e).render(_this.el);
33666 Roo.each(_this.toolbarItems, function(e) {
33667 _this.navgroup.addItem(e);
33671 this.first = this.navgroup.addItem({
33672 tooltip: this.firstText,
33673 cls: "prev btn-outline-secondary",
33674 html : ' <i class="fa fa-step-backward"></i>',
33676 preventDefault: true,
33677 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33680 this.prev = this.navgroup.addItem({
33681 tooltip: this.prevText,
33682 cls: "prev btn-outline-secondary",
33683 html : ' <i class="fa fa-backward"></i>',
33685 preventDefault: true,
33686 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
33688 //this.addSeparator();
33691 var field = this.navgroup.addItem( {
33693 cls : 'x-paging-position btn-outline-secondary',
33695 html : this.beforePageText +
33696 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33697 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
33700 this.field = field.el.select('input', true).first();
33701 this.field.on("keydown", this.onPagingKeydown, this);
33702 this.field.on("focus", function(){this.dom.select();});
33705 this.afterTextEl = field.el.select('.x-paging-after',true).first();
33706 //this.field.setHeight(18);
33707 //this.addSeparator();
33708 this.next = this.navgroup.addItem({
33709 tooltip: this.nextText,
33710 cls: "next btn-outline-secondary",
33711 html : ' <i class="fa fa-forward"></i>',
33713 preventDefault: true,
33714 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
33716 this.last = this.navgroup.addItem({
33717 tooltip: this.lastText,
33718 html : ' <i class="fa fa-step-forward"></i>',
33719 cls: "next btn-outline-secondary",
33721 preventDefault: true,
33722 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
33724 //this.addSeparator();
33725 this.loading = this.navgroup.addItem({
33726 tooltip: this.refreshText,
33727 cls: "btn-outline-secondary",
33728 html : ' <i class="fa fa-refresh"></i>',
33729 preventDefault: true,
33730 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33736 updateInfo : function(){
33737 if(this.displayEl){
33738 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33739 var msg = count == 0 ?
33743 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
33745 this.displayEl.update(msg);
33750 onLoad : function(ds, r, o)
33752 this.cursor = o.params && o.params.start ? o.params.start : 0;
33754 var d = this.getPageData(),
33759 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33760 this.field.dom.value = ap;
33761 this.first.setDisabled(ap == 1);
33762 this.prev.setDisabled(ap == 1);
33763 this.next.setDisabled(ap == ps);
33764 this.last.setDisabled(ap == ps);
33765 this.loading.enable();
33770 getPageData : function(){
33771 var total = this.ds.getTotalCount();
33774 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33775 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33780 onLoadError : function(proxy, o){
33781 this.loading.enable();
33782 if (this.ds.events.loadexception.listeners.length < 2) {
33783 // nothing has been assigned to loadexception except this...
33785 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33791 onPagingKeydown : function(e){
33792 var k = e.getKey();
33793 var d = this.getPageData();
33795 var v = this.field.dom.value, pageNum;
33796 if(!v || isNaN(pageNum = parseInt(v, 10))){
33797 this.field.dom.value = d.activePage;
33800 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33801 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33804 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))
33806 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33807 this.field.dom.value = pageNum;
33808 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33811 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33813 var v = this.field.dom.value, pageNum;
33814 var increment = (e.shiftKey) ? 10 : 1;
33815 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33818 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33819 this.field.dom.value = d.activePage;
33822 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33824 this.field.dom.value = parseInt(v, 10) + increment;
33825 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33826 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33833 beforeLoad : function(){
33835 this.loading.disable();
33840 onClick : function(which){
33849 ds.load({params:{start: 0, limit: this.pageSize}});
33852 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33855 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33858 var total = ds.getTotalCount();
33859 var extra = total % this.pageSize;
33860 var lastStart = extra ? (total - extra) : total-this.pageSize;
33861 ds.load({params:{start: lastStart, limit: this.pageSize}});
33864 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33870 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33871 * @param {Roo.data.Store} store The data store to unbind
33873 unbind : function(ds){
33874 ds.un("beforeload", this.beforeLoad, this);
33875 ds.un("load", this.onLoad, this);
33876 ds.un("loadexception", this.onLoadError, this);
33877 ds.un("remove", this.updateInfo, this);
33878 ds.un("add", this.updateInfo, this);
33879 this.ds = undefined;
33883 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33884 * @param {Roo.data.Store} store The data store to bind
33886 bind : function(ds){
33887 ds.on("beforeload", this.beforeLoad, this);
33888 ds.on("load", this.onLoad, this);
33889 ds.on("loadexception", this.onLoadError, this);
33890 ds.on("remove", this.updateInfo, this);
33891 ds.on("add", this.updateInfo, this);
33902 * @class Roo.bootstrap.MessageBar
33903 * @extends Roo.bootstrap.Component
33904 * Bootstrap MessageBar class
33905 * @cfg {String} html contents of the MessageBar
33906 * @cfg {String} weight (info | success | warning | danger) default info
33907 * @cfg {String} beforeClass insert the bar before the given class
33908 * @cfg {Boolean} closable (true | false) default false
33909 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33912 * Create a new Element
33913 * @param {Object} config The config object
33916 Roo.bootstrap.MessageBar = function(config){
33917 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33920 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33926 beforeClass: 'bootstrap-sticky-wrap',
33928 getAutoCreate : function(){
33932 cls: 'alert alert-dismissable alert-' + this.weight,
33937 html: this.html || ''
33943 cfg.cls += ' alert-messages-fixed';
33957 onRender : function(ct, position)
33959 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33962 var cfg = Roo.apply({}, this.getAutoCreate());
33966 cfg.cls += ' ' + this.cls;
33969 cfg.style = this.style;
33971 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33973 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33976 this.el.select('>button.close').on('click', this.hide, this);
33982 if (!this.rendered) {
33988 this.fireEvent('show', this);
33994 if (!this.rendered) {
34000 this.fireEvent('hide', this);
34003 update : function()
34005 // var e = this.el.dom.firstChild;
34007 // if(this.closable){
34008 // e = e.nextSibling;
34011 // e.data = this.html || '';
34013 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34029 * @class Roo.bootstrap.Graph
34030 * @extends Roo.bootstrap.Component
34031 * Bootstrap Graph class
34035 @cfg {String} graphtype bar | vbar | pie
34036 @cfg {number} g_x coodinator | centre x (pie)
34037 @cfg {number} g_y coodinator | centre y (pie)
34038 @cfg {number} g_r radius (pie)
34039 @cfg {number} g_height height of the chart (respected by all elements in the set)
34040 @cfg {number} g_width width of the chart (respected by all elements in the set)
34041 @cfg {Object} title The title of the chart
34044 -opts (object) options for the chart
34046 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34047 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34049 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.
34050 o stacked (boolean) whether or not to tread values as in a stacked bar chart
34052 o stretch (boolean)
34054 -opts (object) options for the pie
34057 o startAngle (number)
34058 o endAngle (number)
34062 * Create a new Input
34063 * @param {Object} config The config object
34066 Roo.bootstrap.Graph = function(config){
34067 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34073 * The img click event for the img.
34074 * @param {Roo.EventObject} e
34080 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
34091 //g_colors: this.colors,
34098 getAutoCreate : function(){
34109 onRender : function(ct,position){
34112 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34114 if (typeof(Raphael) == 'undefined') {
34115 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34119 this.raphael = Raphael(this.el.dom);
34121 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34122 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34123 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34124 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34126 r.text(160, 10, "Single Series Chart").attr(txtattr);
34127 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34128 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34129 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34131 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34132 r.barchart(330, 10, 300, 220, data1);
34133 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34134 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34137 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34138 // r.barchart(30, 30, 560, 250, xdata, {
34139 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34140 // axis : "0 0 1 1",
34141 // axisxlabels : xdata
34142 // //yvalues : cols,
34145 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34147 // this.load(null,xdata,{
34148 // axis : "0 0 1 1",
34149 // axisxlabels : xdata
34154 load : function(graphtype,xdata,opts)
34156 this.raphael.clear();
34158 graphtype = this.graphtype;
34163 var r = this.raphael,
34164 fin = function () {
34165 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34167 fout = function () {
34168 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34170 pfin = function() {
34171 this.sector.stop();
34172 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34175 this.label[0].stop();
34176 this.label[0].attr({ r: 7.5 });
34177 this.label[1].attr({ "font-weight": 800 });
34180 pfout = function() {
34181 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34184 this.label[0].animate({ r: 5 }, 500, "bounce");
34185 this.label[1].attr({ "font-weight": 400 });
34191 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34194 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34197 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
34198 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34200 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34207 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34212 setTitle: function(o)
34217 initEvents: function() {
34220 this.el.on('click', this.onClick, this);
34224 onClick : function(e)
34226 Roo.log('img onclick');
34227 this.fireEvent('click', this, e);
34233 Roo.bootstrap.dash = {};/*
34239 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34242 * @class Roo.bootstrap.dash.NumberBox
34243 * @extends Roo.bootstrap.Component
34244 * Bootstrap NumberBox class
34245 * @cfg {String} headline Box headline
34246 * @cfg {String} content Box content
34247 * @cfg {String} icon Box icon
34248 * @cfg {String} footer Footer text
34249 * @cfg {String} fhref Footer href
34252 * Create a new NumberBox
34253 * @param {Object} config The config object
34257 Roo.bootstrap.dash.NumberBox = function(config){
34258 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34262 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
34271 getAutoCreate : function(){
34275 cls : 'small-box ',
34283 cls : 'roo-headline',
34284 html : this.headline
34288 cls : 'roo-content',
34289 html : this.content
34303 cls : 'ion ' + this.icon
34312 cls : 'small-box-footer',
34313 href : this.fhref || '#',
34317 cfg.cn.push(footer);
34324 onRender : function(ct,position){
34325 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34332 setHeadline: function (value)
34334 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34337 setFooter: function (value, href)
34339 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34342 this.el.select('a.small-box-footer',true).first().attr('href', href);
34347 setContent: function (value)
34349 this.el.select('.roo-content',true).first().dom.innerHTML = value;
34352 initEvents: function()
34366 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34369 * @class Roo.bootstrap.dash.TabBox
34370 * @extends Roo.bootstrap.Component
34371 * @children Roo.bootstrap.dash.TabPane
34372 * Bootstrap TabBox class
34373 * @cfg {String} title Title of the TabBox
34374 * @cfg {String} icon Icon of the TabBox
34375 * @cfg {Boolean} showtabs (true|false) show the tabs default true
34376 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34379 * Create a new TabBox
34380 * @param {Object} config The config object
34384 Roo.bootstrap.dash.TabBox = function(config){
34385 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34390 * When a pane is added
34391 * @param {Roo.bootstrap.dash.TabPane} pane
34395 * @event activatepane
34396 * When a pane is activated
34397 * @param {Roo.bootstrap.dash.TabPane} pane
34399 "activatepane" : true
34407 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
34412 tabScrollable : false,
34414 getChildContainer : function()
34416 return this.el.select('.tab-content', true).first();
34419 getAutoCreate : function(){
34423 cls: 'pull-left header',
34431 cls: 'fa ' + this.icon
34437 cls: 'nav nav-tabs pull-right',
34443 if(this.tabScrollable){
34450 cls: 'nav nav-tabs pull-right',
34461 cls: 'nav-tabs-custom',
34466 cls: 'tab-content no-padding',
34474 initEvents : function()
34476 //Roo.log('add add pane handler');
34477 this.on('addpane', this.onAddPane, this);
34480 * Updates the box title
34481 * @param {String} html to set the title to.
34483 setTitle : function(value)
34485 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34487 onAddPane : function(pane)
34489 this.panes.push(pane);
34490 //Roo.log('addpane');
34492 // tabs are rendere left to right..
34493 if(!this.showtabs){
34497 var ctr = this.el.select('.nav-tabs', true).first();
34500 var existing = ctr.select('.nav-tab',true);
34501 var qty = existing.getCount();;
34504 var tab = ctr.createChild({
34506 cls : 'nav-tab' + (qty ? '' : ' active'),
34514 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34517 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34519 pane.el.addClass('active');
34524 onTabClick : function(ev,un,ob,pane)
34526 //Roo.log('tab - prev default');
34527 ev.preventDefault();
34530 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34531 pane.tab.addClass('active');
34532 //Roo.log(pane.title);
34533 this.getChildContainer().select('.tab-pane',true).removeClass('active');
34534 // technically we should have a deactivate event.. but maybe add later.
34535 // and it should not de-activate the selected tab...
34536 this.fireEvent('activatepane', pane);
34537 pane.el.addClass('active');
34538 pane.fireEvent('activate');
34543 getActivePane : function()
34546 Roo.each(this.panes, function(p) {
34547 if(p.el.hasClass('active')){
34568 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34570 * @class Roo.bootstrap.TabPane
34571 * @extends Roo.bootstrap.Component
34572 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
34573 * Bootstrap TabPane class
34574 * @cfg {Boolean} active (false | true) Default false
34575 * @cfg {String} title title of panel
34579 * Create a new TabPane
34580 * @param {Object} config The config object
34583 Roo.bootstrap.dash.TabPane = function(config){
34584 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34590 * When a pane is activated
34591 * @param {Roo.bootstrap.dash.TabPane} pane
34598 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
34603 // the tabBox that this is attached to.
34606 getAutoCreate : function()
34614 cfg.cls += ' active';
34619 initEvents : function()
34621 //Roo.log('trigger add pane handler');
34622 this.parent().fireEvent('addpane', this)
34626 * Updates the tab title
34627 * @param {String} html to set the title to.
34629 setTitle: function(str)
34635 this.tab.select('a', true).first().dom.innerHTML = str;
34654 * @class Roo.bootstrap.Tooltip
34655 * Bootstrap Tooltip class
34656 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34657 * to determine which dom element triggers the tooltip.
34659 * It needs to add support for additional attributes like tooltip-position
34662 * Create a new Toolti
34663 * @param {Object} config The config object
34666 Roo.bootstrap.Tooltip = function(config){
34667 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34669 this.alignment = Roo.bootstrap.Tooltip.alignment;
34671 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34672 this.alignment = config.alignment;
34677 Roo.apply(Roo.bootstrap.Tooltip, {
34679 * @function init initialize tooltip monitoring.
34683 currentTip : false,
34684 currentRegion : false,
34690 Roo.get(document).on('mouseover', this.enter ,this);
34691 Roo.get(document).on('mouseout', this.leave, this);
34694 this.currentTip = new Roo.bootstrap.Tooltip();
34697 enter : function(ev)
34699 var dom = ev.getTarget();
34701 //Roo.log(['enter',dom]);
34702 var el = Roo.fly(dom);
34703 if (this.currentEl) {
34705 //Roo.log(this.currentEl);
34706 //Roo.log(this.currentEl.contains(dom));
34707 if (this.currentEl == el) {
34710 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34716 if (this.currentTip.el) {
34717 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34721 if(!el || el.dom == document){
34727 if (!el.attr('tooltip')) {
34728 pel = el.findParent("[tooltip]");
34730 bindEl = Roo.get(pel);
34736 // you can not look for children, as if el is the body.. then everythign is the child..
34737 if (!pel && !el.attr('tooltip')) { //
34738 if (!el.select("[tooltip]").elements.length) {
34741 // is the mouse over this child...?
34742 bindEl = el.select("[tooltip]").first();
34743 var xy = ev.getXY();
34744 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34745 //Roo.log("not in region.");
34748 //Roo.log("child element over..");
34751 this.currentEl = el;
34752 this.currentTip.bind(bindEl);
34753 this.currentRegion = Roo.lib.Region.getRegion(dom);
34754 this.currentTip.enter();
34757 leave : function(ev)
34759 var dom = ev.getTarget();
34760 //Roo.log(['leave',dom]);
34761 if (!this.currentEl) {
34766 if (dom != this.currentEl.dom) {
34769 var xy = ev.getXY();
34770 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
34773 // only activate leave if mouse cursor is outside... bounding box..
34778 if (this.currentTip) {
34779 this.currentTip.leave();
34781 //Roo.log('clear currentEl');
34782 this.currentEl = false;
34787 'left' : ['r-l', [-2,0], 'right'],
34788 'right' : ['l-r', [2,0], 'left'],
34789 'bottom' : ['t-b', [0,2], 'top'],
34790 'top' : [ 'b-t', [0,-2], 'bottom']
34796 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
34801 delay : null, // can be { show : 300 , hide: 500}
34805 hoverState : null, //???
34807 placement : 'bottom',
34811 getAutoCreate : function(){
34818 cls : 'tooltip-arrow arrow'
34821 cls : 'tooltip-inner'
34828 bind : function(el)
34833 initEvents : function()
34835 this.arrowEl = this.el.select('.arrow', true).first();
34836 this.innerEl = this.el.select('.tooltip-inner', true).first();
34839 enter : function () {
34841 if (this.timeout != null) {
34842 clearTimeout(this.timeout);
34845 this.hoverState = 'in';
34846 //Roo.log("enter - show");
34847 if (!this.delay || !this.delay.show) {
34852 this.timeout = setTimeout(function () {
34853 if (_t.hoverState == 'in') {
34856 }, this.delay.show);
34860 clearTimeout(this.timeout);
34862 this.hoverState = 'out';
34863 if (!this.delay || !this.delay.hide) {
34869 this.timeout = setTimeout(function () {
34870 //Roo.log("leave - timeout");
34872 if (_t.hoverState == 'out') {
34874 Roo.bootstrap.Tooltip.currentEl = false;
34879 show : function (msg)
34882 this.render(document.body);
34885 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34887 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34889 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34891 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34892 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34894 if(this.bindEl.attr('tooltip-class')) {
34895 this.el.addClass(this.bindEl.attr('tooltip-class'));
34898 var placement = typeof this.placement == 'function' ?
34899 this.placement.call(this, this.el, on_el) :
34902 if(this.bindEl.attr('tooltip-placement')) {
34903 placement = this.bindEl.attr('tooltip-placement');
34906 var autoToken = /\s?auto?\s?/i;
34907 var autoPlace = autoToken.test(placement);
34909 placement = placement.replace(autoToken, '') || 'top';
34913 //this.el.setXY([0,0]);
34915 //this.el.dom.style.display='block';
34917 //this.el.appendTo(on_el);
34919 var p = this.getPosition();
34920 var box = this.el.getBox();
34926 var align = this.alignment[placement];
34928 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34930 if(placement == 'top' || placement == 'bottom'){
34932 placement = 'right';
34935 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34936 placement = 'left';
34939 var scroll = Roo.select('body', true).first().getScroll();
34941 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34945 align = this.alignment[placement];
34947 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34951 var elems = document.getElementsByTagName('div');
34952 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34953 for (var i = 0; i < elems.length; i++) {
34954 var zindex = Number.parseInt(
34955 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34958 if (zindex > highest) {
34965 this.el.dom.style.zIndex = highest;
34967 this.el.alignTo(this.bindEl, align[0],align[1]);
34968 //var arrow = this.el.select('.arrow',true).first();
34969 //arrow.set(align[2],
34971 this.el.addClass(placement);
34972 this.el.addClass("bs-tooltip-"+ placement);
34974 this.el.addClass('in fade show');
34976 this.hoverState = null;
34978 if (this.el.hasClass('fade')) {
34993 //this.el.setXY([0,0]);
34994 if(this.bindEl.attr('tooltip-class')) {
34995 this.el.removeClass(this.bindEl.attr('tooltip-class'));
34997 this.el.removeClass(['show', 'in']);
35013 * @class Roo.bootstrap.LocationPicker
35014 * @extends Roo.bootstrap.Component
35015 * Bootstrap LocationPicker class
35016 * @cfg {Number} latitude Position when init default 0
35017 * @cfg {Number} longitude Position when init default 0
35018 * @cfg {Number} zoom default 15
35019 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35020 * @cfg {Boolean} mapTypeControl default false
35021 * @cfg {Boolean} disableDoubleClickZoom default false
35022 * @cfg {Boolean} scrollwheel default true
35023 * @cfg {Boolean} streetViewControl default false
35024 * @cfg {Number} radius default 0
35025 * @cfg {String} locationName
35026 * @cfg {Boolean} draggable default true
35027 * @cfg {Boolean} enableAutocomplete default false
35028 * @cfg {Boolean} enableReverseGeocode default true
35029 * @cfg {String} markerTitle
35032 * Create a new LocationPicker
35033 * @param {Object} config The config object
35037 Roo.bootstrap.LocationPicker = function(config){
35039 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35044 * Fires when the picker initialized.
35045 * @param {Roo.bootstrap.LocationPicker} this
35046 * @param {Google Location} location
35050 * @event positionchanged
35051 * Fires when the picker position changed.
35052 * @param {Roo.bootstrap.LocationPicker} this
35053 * @param {Google Location} location
35055 positionchanged : true,
35058 * Fires when the map resize.
35059 * @param {Roo.bootstrap.LocationPicker} this
35064 * Fires when the map show.
35065 * @param {Roo.bootstrap.LocationPicker} this
35070 * Fires when the map hide.
35071 * @param {Roo.bootstrap.LocationPicker} this
35076 * Fires when click the map.
35077 * @param {Roo.bootstrap.LocationPicker} this
35078 * @param {Map event} e
35082 * @event mapRightClick
35083 * Fires when right click the map.
35084 * @param {Roo.bootstrap.LocationPicker} this
35085 * @param {Map event} e
35087 mapRightClick : true,
35089 * @event markerClick
35090 * Fires when click the marker.
35091 * @param {Roo.bootstrap.LocationPicker} this
35092 * @param {Map event} e
35094 markerClick : true,
35096 * @event markerRightClick
35097 * Fires when right click the marker.
35098 * @param {Roo.bootstrap.LocationPicker} this
35099 * @param {Map event} e
35101 markerRightClick : true,
35103 * @event OverlayViewDraw
35104 * Fires when OverlayView Draw
35105 * @param {Roo.bootstrap.LocationPicker} this
35107 OverlayViewDraw : true,
35109 * @event OverlayViewOnAdd
35110 * Fires when OverlayView Draw
35111 * @param {Roo.bootstrap.LocationPicker} this
35113 OverlayViewOnAdd : true,
35115 * @event OverlayViewOnRemove
35116 * Fires when OverlayView Draw
35117 * @param {Roo.bootstrap.LocationPicker} this
35119 OverlayViewOnRemove : true,
35121 * @event OverlayViewShow
35122 * Fires when OverlayView Draw
35123 * @param {Roo.bootstrap.LocationPicker} this
35124 * @param {Pixel} cpx
35126 OverlayViewShow : true,
35128 * @event OverlayViewHide
35129 * Fires when OverlayView Draw
35130 * @param {Roo.bootstrap.LocationPicker} this
35132 OverlayViewHide : true,
35134 * @event loadexception
35135 * Fires when load google lib failed.
35136 * @param {Roo.bootstrap.LocationPicker} this
35138 loadexception : true
35143 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
35145 gMapContext: false,
35151 mapTypeControl: false,
35152 disableDoubleClickZoom: false,
35154 streetViewControl: false,
35158 enableAutocomplete: false,
35159 enableReverseGeocode: true,
35162 getAutoCreate: function()
35167 cls: 'roo-location-picker'
35173 initEvents: function(ct, position)
35175 if(!this.el.getWidth() || this.isApplied()){
35179 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35184 initial: function()
35186 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35187 this.fireEvent('loadexception', this);
35191 if(!this.mapTypeId){
35192 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35195 this.gMapContext = this.GMapContext();
35197 this.initOverlayView();
35199 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35203 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35204 _this.setPosition(_this.gMapContext.marker.position);
35207 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35208 _this.fireEvent('mapClick', this, event);
35212 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35213 _this.fireEvent('mapRightClick', this, event);
35217 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35218 _this.fireEvent('markerClick', this, event);
35222 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35223 _this.fireEvent('markerRightClick', this, event);
35227 this.setPosition(this.gMapContext.location);
35229 this.fireEvent('initial', this, this.gMapContext.location);
35232 initOverlayView: function()
35236 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35240 _this.fireEvent('OverlayViewDraw', _this);
35245 _this.fireEvent('OverlayViewOnAdd', _this);
35248 onRemove: function()
35250 _this.fireEvent('OverlayViewOnRemove', _this);
35253 show: function(cpx)
35255 _this.fireEvent('OverlayViewShow', _this, cpx);
35260 _this.fireEvent('OverlayViewHide', _this);
35266 fromLatLngToContainerPixel: function(event)
35268 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35271 isApplied: function()
35273 return this.getGmapContext() == false ? false : true;
35276 getGmapContext: function()
35278 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35281 GMapContext: function()
35283 var position = new google.maps.LatLng(this.latitude, this.longitude);
35285 var _map = new google.maps.Map(this.el.dom, {
35288 mapTypeId: this.mapTypeId,
35289 mapTypeControl: this.mapTypeControl,
35290 disableDoubleClickZoom: this.disableDoubleClickZoom,
35291 scrollwheel: this.scrollwheel,
35292 streetViewControl: this.streetViewControl,
35293 locationName: this.locationName,
35294 draggable: this.draggable,
35295 enableAutocomplete: this.enableAutocomplete,
35296 enableReverseGeocode: this.enableReverseGeocode
35299 var _marker = new google.maps.Marker({
35300 position: position,
35302 title: this.markerTitle,
35303 draggable: this.draggable
35310 location: position,
35311 radius: this.radius,
35312 locationName: this.locationName,
35313 addressComponents: {
35314 formatted_address: null,
35315 addressLine1: null,
35316 addressLine2: null,
35318 streetNumber: null,
35322 stateOrProvince: null
35325 domContainer: this.el.dom,
35326 geodecoder: new google.maps.Geocoder()
35330 drawCircle: function(center, radius, options)
35332 if (this.gMapContext.circle != null) {
35333 this.gMapContext.circle.setMap(null);
35337 options = Roo.apply({}, options, {
35338 strokeColor: "#0000FF",
35339 strokeOpacity: .35,
35341 fillColor: "#0000FF",
35345 options.map = this.gMapContext.map;
35346 options.radius = radius;
35347 options.center = center;
35348 this.gMapContext.circle = new google.maps.Circle(options);
35349 return this.gMapContext.circle;
35355 setPosition: function(location)
35357 this.gMapContext.location = location;
35358 this.gMapContext.marker.setPosition(location);
35359 this.gMapContext.map.panTo(location);
35360 this.drawCircle(location, this.gMapContext.radius, {});
35364 if (this.gMapContext.settings.enableReverseGeocode) {
35365 this.gMapContext.geodecoder.geocode({
35366 latLng: this.gMapContext.location
35367 }, function(results, status) {
35369 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35370 _this.gMapContext.locationName = results[0].formatted_address;
35371 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35373 _this.fireEvent('positionchanged', this, location);
35380 this.fireEvent('positionchanged', this, location);
35385 google.maps.event.trigger(this.gMapContext.map, "resize");
35387 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35389 this.fireEvent('resize', this);
35392 setPositionByLatLng: function(latitude, longitude)
35394 this.setPosition(new google.maps.LatLng(latitude, longitude));
35397 getCurrentPosition: function()
35400 latitude: this.gMapContext.location.lat(),
35401 longitude: this.gMapContext.location.lng()
35405 getAddressName: function()
35407 return this.gMapContext.locationName;
35410 getAddressComponents: function()
35412 return this.gMapContext.addressComponents;
35415 address_component_from_google_geocode: function(address_components)
35419 for (var i = 0; i < address_components.length; i++) {
35420 var component = address_components[i];
35421 if (component.types.indexOf("postal_code") >= 0) {
35422 result.postalCode = component.short_name;
35423 } else if (component.types.indexOf("street_number") >= 0) {
35424 result.streetNumber = component.short_name;
35425 } else if (component.types.indexOf("route") >= 0) {
35426 result.streetName = component.short_name;
35427 } else if (component.types.indexOf("neighborhood") >= 0) {
35428 result.city = component.short_name;
35429 } else if (component.types.indexOf("locality") >= 0) {
35430 result.city = component.short_name;
35431 } else if (component.types.indexOf("sublocality") >= 0) {
35432 result.district = component.short_name;
35433 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35434 result.stateOrProvince = component.short_name;
35435 } else if (component.types.indexOf("country") >= 0) {
35436 result.country = component.short_name;
35440 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35441 result.addressLine2 = "";
35445 setZoomLevel: function(zoom)
35447 this.gMapContext.map.setZoom(zoom);
35460 this.fireEvent('show', this);
35471 this.fireEvent('hide', this);
35476 Roo.apply(Roo.bootstrap.LocationPicker, {
35478 OverlayView : function(map, options)
35480 options = options || {};
35487 * @class Roo.bootstrap.Alert
35488 * @extends Roo.bootstrap.Component
35489 * Bootstrap Alert class - shows an alert area box
35491 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35492 Enter a valid email address
35495 * @cfg {String} title The title of alert
35496 * @cfg {String} html The content of alert
35497 * @cfg {String} weight (success|info|warning|danger) Weight of the message
35498 * @cfg {String} fa font-awesomeicon
35499 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35500 * @cfg {Boolean} close true to show a x closer
35504 * Create a new alert
35505 * @param {Object} config The config object
35509 Roo.bootstrap.Alert = function(config){
35510 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35514 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
35520 faicon: false, // BC
35524 getAutoCreate : function()
35536 style : this.close ? '' : 'display:none'
35540 cls : 'roo-alert-icon'
35545 cls : 'roo-alert-title',
35550 cls : 'roo-alert-text',
35557 cfg.cn[0].cls += ' fa ' + this.faicon;
35560 cfg.cn[0].cls += ' fa ' + this.fa;
35564 cfg.cls += ' alert-' + this.weight;
35570 initEvents: function()
35572 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35573 this.titleEl = this.el.select('.roo-alert-title',true).first();
35574 this.iconEl = this.el.select('.roo-alert-icon',true).first();
35575 this.htmlEl = this.el.select('.roo-alert-text',true).first();
35576 if (this.seconds > 0) {
35577 this.hide.defer(this.seconds, this);
35581 * Set the Title Message HTML
35582 * @param {String} html
35584 setTitle : function(str)
35586 this.titleEl.dom.innerHTML = str;
35590 * Set the Body Message HTML
35591 * @param {String} html
35593 setHtml : function(str)
35595 this.htmlEl.dom.innerHTML = str;
35598 * Set the Weight of the alert
35599 * @param {String} (success|info|warning|danger) weight
35602 setWeight : function(weight)
35605 this.el.removeClass('alert-' + this.weight);
35608 this.weight = weight;
35610 this.el.addClass('alert-' + this.weight);
35613 * Set the Icon of the alert
35614 * @param {String} see fontawsome names (name without the 'fa-' bit)
35616 setIcon : function(icon)
35619 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35622 this.faicon = icon;
35624 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35649 * @class Roo.bootstrap.UploadCropbox
35650 * @extends Roo.bootstrap.Component
35651 * Bootstrap UploadCropbox class
35652 * @cfg {String} emptyText show when image has been loaded
35653 * @cfg {String} rotateNotify show when image too small to rotate
35654 * @cfg {Number} errorTimeout default 3000
35655 * @cfg {Number} minWidth default 300
35656 * @cfg {Number} minHeight default 300
35657 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35658 * @cfg {Boolean} isDocument (true|false) default false
35659 * @cfg {String} url action url
35660 * @cfg {String} paramName default 'imageUpload'
35661 * @cfg {String} method default POST
35662 * @cfg {Boolean} loadMask (true|false) default true
35663 * @cfg {Boolean} loadingText default 'Loading...'
35666 * Create a new UploadCropbox
35667 * @param {Object} config The config object
35670 Roo.bootstrap.UploadCropbox = function(config){
35671 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35675 * @event beforeselectfile
35676 * Fire before select file
35677 * @param {Roo.bootstrap.UploadCropbox} this
35679 "beforeselectfile" : true,
35682 * Fire after initEvent
35683 * @param {Roo.bootstrap.UploadCropbox} this
35688 * Fire after initEvent
35689 * @param {Roo.bootstrap.UploadCropbox} this
35690 * @param {String} data
35695 * Fire when preparing the file data
35696 * @param {Roo.bootstrap.UploadCropbox} this
35697 * @param {Object} file
35702 * Fire when get exception
35703 * @param {Roo.bootstrap.UploadCropbox} this
35704 * @param {XMLHttpRequest} xhr
35706 "exception" : true,
35708 * @event beforeloadcanvas
35709 * Fire before load the canvas
35710 * @param {Roo.bootstrap.UploadCropbox} this
35711 * @param {String} src
35713 "beforeloadcanvas" : true,
35716 * Fire when trash image
35717 * @param {Roo.bootstrap.UploadCropbox} this
35722 * Fire when download the image
35723 * @param {Roo.bootstrap.UploadCropbox} this
35727 * @event footerbuttonclick
35728 * Fire when footerbuttonclick
35729 * @param {Roo.bootstrap.UploadCropbox} this
35730 * @param {String} type
35732 "footerbuttonclick" : true,
35736 * @param {Roo.bootstrap.UploadCropbox} this
35741 * Fire when rotate the image
35742 * @param {Roo.bootstrap.UploadCropbox} this
35743 * @param {String} pos
35748 * Fire when inspect the file
35749 * @param {Roo.bootstrap.UploadCropbox} this
35750 * @param {Object} file
35755 * Fire when xhr upload the file
35756 * @param {Roo.bootstrap.UploadCropbox} this
35757 * @param {Object} data
35762 * Fire when arrange the file data
35763 * @param {Roo.bootstrap.UploadCropbox} this
35764 * @param {Object} formData
35769 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35772 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
35774 emptyText : 'Click to upload image',
35775 rotateNotify : 'Image is too small to rotate',
35776 errorTimeout : 3000,
35790 cropType : 'image/jpeg',
35792 canvasLoaded : false,
35793 isDocument : false,
35795 paramName : 'imageUpload',
35797 loadingText : 'Loading...',
35800 getAutoCreate : function()
35804 cls : 'roo-upload-cropbox',
35808 cls : 'roo-upload-cropbox-selector',
35813 cls : 'roo-upload-cropbox-body',
35814 style : 'cursor:pointer',
35818 cls : 'roo-upload-cropbox-preview'
35822 cls : 'roo-upload-cropbox-thumb'
35826 cls : 'roo-upload-cropbox-empty-notify',
35827 html : this.emptyText
35831 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35832 html : this.rotateNotify
35838 cls : 'roo-upload-cropbox-footer',
35841 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35851 onRender : function(ct, position)
35853 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35855 if (this.buttons.length) {
35857 Roo.each(this.buttons, function(bb) {
35859 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35861 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35867 this.maskEl = this.el;
35871 initEvents : function()
35873 this.urlAPI = (window.createObjectURL && window) ||
35874 (window.URL && URL.revokeObjectURL && URL) ||
35875 (window.webkitURL && webkitURL);
35877 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35878 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35880 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35881 this.selectorEl.hide();
35883 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35884 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35886 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35887 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35888 this.thumbEl.hide();
35890 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35891 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35893 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35894 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35895 this.errorEl.hide();
35897 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35898 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35899 this.footerEl.hide();
35901 this.setThumbBoxSize();
35907 this.fireEvent('initial', this);
35914 window.addEventListener("resize", function() { _this.resize(); } );
35916 this.bodyEl.on('click', this.beforeSelectFile, this);
35919 this.bodyEl.on('touchstart', this.onTouchStart, this);
35920 this.bodyEl.on('touchmove', this.onTouchMove, this);
35921 this.bodyEl.on('touchend', this.onTouchEnd, this);
35925 this.bodyEl.on('mousedown', this.onMouseDown, this);
35926 this.bodyEl.on('mousemove', this.onMouseMove, this);
35927 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35928 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35929 Roo.get(document).on('mouseup', this.onMouseUp, this);
35932 this.selectorEl.on('change', this.onFileSelected, this);
35938 this.baseScale = 1;
35940 this.baseRotate = 1;
35941 this.dragable = false;
35942 this.pinching = false;
35945 this.cropData = false;
35946 this.notifyEl.dom.innerHTML = this.emptyText;
35948 this.selectorEl.dom.value = '';
35952 resize : function()
35954 if(this.fireEvent('resize', this) != false){
35955 this.setThumbBoxPosition();
35956 this.setCanvasPosition();
35960 onFooterButtonClick : function(e, el, o, type)
35963 case 'rotate-left' :
35964 this.onRotateLeft(e);
35966 case 'rotate-right' :
35967 this.onRotateRight(e);
35970 this.beforeSelectFile(e);
35985 this.fireEvent('footerbuttonclick', this, type);
35988 beforeSelectFile : function(e)
35990 e.preventDefault();
35992 if(this.fireEvent('beforeselectfile', this) != false){
35993 this.selectorEl.dom.click();
35997 onFileSelected : function(e)
35999 e.preventDefault();
36001 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36005 var file = this.selectorEl.dom.files[0];
36007 if(this.fireEvent('inspect', this, file) != false){
36008 this.prepare(file);
36013 trash : function(e)
36015 this.fireEvent('trash', this);
36018 download : function(e)
36020 this.fireEvent('download', this);
36023 loadCanvas : function(src)
36025 if(this.fireEvent('beforeloadcanvas', this, src) != false){
36029 this.imageEl = document.createElement('img');
36033 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36035 this.imageEl.src = src;
36039 onLoadCanvas : function()
36041 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36042 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36044 this.bodyEl.un('click', this.beforeSelectFile, this);
36046 this.notifyEl.hide();
36047 this.thumbEl.show();
36048 this.footerEl.show();
36050 this.baseRotateLevel();
36052 if(this.isDocument){
36053 this.setThumbBoxSize();
36056 this.setThumbBoxPosition();
36058 this.baseScaleLevel();
36064 this.canvasLoaded = true;
36067 this.maskEl.unmask();
36072 setCanvasPosition : function()
36074 if(!this.canvasEl){
36078 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36079 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36081 this.previewEl.setLeft(pw);
36082 this.previewEl.setTop(ph);
36086 onMouseDown : function(e)
36090 this.dragable = true;
36091 this.pinching = false;
36093 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36094 this.dragable = false;
36098 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36099 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36103 onMouseMove : function(e)
36107 if(!this.canvasLoaded){
36111 if (!this.dragable){
36115 var minX = Math.ceil(this.thumbEl.getLeft(true));
36116 var minY = Math.ceil(this.thumbEl.getTop(true));
36118 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36119 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36121 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36122 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36124 x = x - this.mouseX;
36125 y = y - this.mouseY;
36127 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36128 var bgY = Math.ceil(y + this.previewEl.getTop(true));
36130 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36131 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36133 this.previewEl.setLeft(bgX);
36134 this.previewEl.setTop(bgY);
36136 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36137 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36140 onMouseUp : function(e)
36144 this.dragable = false;
36147 onMouseWheel : function(e)
36151 this.startScale = this.scale;
36153 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36155 if(!this.zoomable()){
36156 this.scale = this.startScale;
36165 zoomable : function()
36167 var minScale = this.thumbEl.getWidth() / this.minWidth;
36169 if(this.minWidth < this.minHeight){
36170 minScale = this.thumbEl.getHeight() / this.minHeight;
36173 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36174 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36178 (this.rotate == 0 || this.rotate == 180) &&
36180 width > this.imageEl.OriginWidth ||
36181 height > this.imageEl.OriginHeight ||
36182 (width < this.minWidth && height < this.minHeight)
36190 (this.rotate == 90 || this.rotate == 270) &&
36192 width > this.imageEl.OriginWidth ||
36193 height > this.imageEl.OriginHeight ||
36194 (width < this.minHeight && height < this.minWidth)
36201 !this.isDocument &&
36202 (this.rotate == 0 || this.rotate == 180) &&
36204 width < this.minWidth ||
36205 width > this.imageEl.OriginWidth ||
36206 height < this.minHeight ||
36207 height > this.imageEl.OriginHeight
36214 !this.isDocument &&
36215 (this.rotate == 90 || this.rotate == 270) &&
36217 width < this.minHeight ||
36218 width > this.imageEl.OriginWidth ||
36219 height < this.minWidth ||
36220 height > this.imageEl.OriginHeight
36230 onRotateLeft : function(e)
36232 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36234 var minScale = this.thumbEl.getWidth() / this.minWidth;
36236 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36237 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36239 this.startScale = this.scale;
36241 while (this.getScaleLevel() < minScale){
36243 this.scale = this.scale + 1;
36245 if(!this.zoomable()){
36250 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36251 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36256 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36263 this.scale = this.startScale;
36265 this.onRotateFail();
36270 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36272 if(this.isDocument){
36273 this.setThumbBoxSize();
36274 this.setThumbBoxPosition();
36275 this.setCanvasPosition();
36280 this.fireEvent('rotate', this, 'left');
36284 onRotateRight : function(e)
36286 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36288 var minScale = this.thumbEl.getWidth() / this.minWidth;
36290 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36291 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36293 this.startScale = this.scale;
36295 while (this.getScaleLevel() < minScale){
36297 this.scale = this.scale + 1;
36299 if(!this.zoomable()){
36304 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36305 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36310 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36317 this.scale = this.startScale;
36319 this.onRotateFail();
36324 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36326 if(this.isDocument){
36327 this.setThumbBoxSize();
36328 this.setThumbBoxPosition();
36329 this.setCanvasPosition();
36334 this.fireEvent('rotate', this, 'right');
36337 onRotateFail : function()
36339 this.errorEl.show(true);
36343 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36348 this.previewEl.dom.innerHTML = '';
36350 var canvasEl = document.createElement("canvas");
36352 var contextEl = canvasEl.getContext("2d");
36354 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36355 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36356 var center = this.imageEl.OriginWidth / 2;
36358 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36359 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36360 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36361 center = this.imageEl.OriginHeight / 2;
36364 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36366 contextEl.translate(center, center);
36367 contextEl.rotate(this.rotate * Math.PI / 180);
36369 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36371 this.canvasEl = document.createElement("canvas");
36373 this.contextEl = this.canvasEl.getContext("2d");
36375 switch (this.rotate) {
36378 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36379 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36381 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36386 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36387 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36389 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36390 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);
36394 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36399 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36400 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36402 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36403 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);
36407 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);
36412 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36413 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36415 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36416 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36420 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);
36427 this.previewEl.appendChild(this.canvasEl);
36429 this.setCanvasPosition();
36434 if(!this.canvasLoaded){
36438 var imageCanvas = document.createElement("canvas");
36440 var imageContext = imageCanvas.getContext("2d");
36442 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36443 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36445 var center = imageCanvas.width / 2;
36447 imageContext.translate(center, center);
36449 imageContext.rotate(this.rotate * Math.PI / 180);
36451 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36453 var canvas = document.createElement("canvas");
36455 var context = canvas.getContext("2d");
36457 canvas.width = this.minWidth;
36458 canvas.height = this.minHeight;
36460 switch (this.rotate) {
36463 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36464 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36466 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36467 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36469 var targetWidth = this.minWidth - 2 * x;
36470 var targetHeight = this.minHeight - 2 * y;
36474 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36475 scale = targetWidth / width;
36478 if(x > 0 && y == 0){
36479 scale = targetHeight / height;
36482 if(x > 0 && y > 0){
36483 scale = targetWidth / width;
36485 if(width < height){
36486 scale = targetHeight / height;
36490 context.scale(scale, scale);
36492 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36493 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36495 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36496 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36498 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36503 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36504 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36506 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36507 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36509 var targetWidth = this.minWidth - 2 * x;
36510 var targetHeight = this.minHeight - 2 * y;
36514 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36515 scale = targetWidth / width;
36518 if(x > 0 && y == 0){
36519 scale = targetHeight / height;
36522 if(x > 0 && y > 0){
36523 scale = targetWidth / width;
36525 if(width < height){
36526 scale = targetHeight / height;
36530 context.scale(scale, scale);
36532 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36533 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36535 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36536 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36538 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36540 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36545 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36546 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36548 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36549 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36551 var targetWidth = this.minWidth - 2 * x;
36552 var targetHeight = this.minHeight - 2 * y;
36556 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36557 scale = targetWidth / width;
36560 if(x > 0 && y == 0){
36561 scale = targetHeight / height;
36564 if(x > 0 && y > 0){
36565 scale = targetWidth / width;
36567 if(width < height){
36568 scale = targetHeight / height;
36572 context.scale(scale, scale);
36574 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36575 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36577 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36578 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36580 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36581 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36583 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36588 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36589 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36591 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36592 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36594 var targetWidth = this.minWidth - 2 * x;
36595 var targetHeight = this.minHeight - 2 * y;
36599 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36600 scale = targetWidth / width;
36603 if(x > 0 && y == 0){
36604 scale = targetHeight / height;
36607 if(x > 0 && y > 0){
36608 scale = targetWidth / width;
36610 if(width < height){
36611 scale = targetHeight / height;
36615 context.scale(scale, scale);
36617 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36618 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36620 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36621 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36623 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36625 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36632 this.cropData = canvas.toDataURL(this.cropType);
36634 if(this.fireEvent('crop', this, this.cropData) !== false){
36635 this.process(this.file, this.cropData);
36642 setThumbBoxSize : function()
36646 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36647 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36648 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36650 this.minWidth = width;
36651 this.minHeight = height;
36653 if(this.rotate == 90 || this.rotate == 270){
36654 this.minWidth = height;
36655 this.minHeight = width;
36660 width = Math.ceil(this.minWidth * height / this.minHeight);
36662 if(this.minWidth > this.minHeight){
36664 height = Math.ceil(this.minHeight * width / this.minWidth);
36667 this.thumbEl.setStyle({
36668 width : width + 'px',
36669 height : height + 'px'
36676 setThumbBoxPosition : function()
36678 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36679 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36681 this.thumbEl.setLeft(x);
36682 this.thumbEl.setTop(y);
36686 baseRotateLevel : function()
36688 this.baseRotate = 1;
36691 typeof(this.exif) != 'undefined' &&
36692 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36693 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36695 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36698 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36702 baseScaleLevel : function()
36706 if(this.isDocument){
36708 if(this.baseRotate == 6 || this.baseRotate == 8){
36710 height = this.thumbEl.getHeight();
36711 this.baseScale = height / this.imageEl.OriginWidth;
36713 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36714 width = this.thumbEl.getWidth();
36715 this.baseScale = width / this.imageEl.OriginHeight;
36721 height = this.thumbEl.getHeight();
36722 this.baseScale = height / this.imageEl.OriginHeight;
36724 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36725 width = this.thumbEl.getWidth();
36726 this.baseScale = width / this.imageEl.OriginWidth;
36732 if(this.baseRotate == 6 || this.baseRotate == 8){
36734 width = this.thumbEl.getHeight();
36735 this.baseScale = width / this.imageEl.OriginHeight;
36737 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36738 height = this.thumbEl.getWidth();
36739 this.baseScale = height / this.imageEl.OriginHeight;
36742 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36743 height = this.thumbEl.getWidth();
36744 this.baseScale = height / this.imageEl.OriginHeight;
36746 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36747 width = this.thumbEl.getHeight();
36748 this.baseScale = width / this.imageEl.OriginWidth;
36755 width = this.thumbEl.getWidth();
36756 this.baseScale = width / this.imageEl.OriginWidth;
36758 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36759 height = this.thumbEl.getHeight();
36760 this.baseScale = height / this.imageEl.OriginHeight;
36763 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36765 height = this.thumbEl.getHeight();
36766 this.baseScale = height / this.imageEl.OriginHeight;
36768 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36769 width = this.thumbEl.getWidth();
36770 this.baseScale = width / this.imageEl.OriginWidth;
36778 getScaleLevel : function()
36780 return this.baseScale * Math.pow(1.1, this.scale);
36783 onTouchStart : function(e)
36785 if(!this.canvasLoaded){
36786 this.beforeSelectFile(e);
36790 var touches = e.browserEvent.touches;
36796 if(touches.length == 1){
36797 this.onMouseDown(e);
36801 if(touches.length != 2){
36807 for(var i = 0, finger; finger = touches[i]; i++){
36808 coords.push(finger.pageX, finger.pageY);
36811 var x = Math.pow(coords[0] - coords[2], 2);
36812 var y = Math.pow(coords[1] - coords[3], 2);
36814 this.startDistance = Math.sqrt(x + y);
36816 this.startScale = this.scale;
36818 this.pinching = true;
36819 this.dragable = false;
36823 onTouchMove : function(e)
36825 if(!this.pinching && !this.dragable){
36829 var touches = e.browserEvent.touches;
36836 this.onMouseMove(e);
36842 for(var i = 0, finger; finger = touches[i]; i++){
36843 coords.push(finger.pageX, finger.pageY);
36846 var x = Math.pow(coords[0] - coords[2], 2);
36847 var y = Math.pow(coords[1] - coords[3], 2);
36849 this.endDistance = Math.sqrt(x + y);
36851 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36853 if(!this.zoomable()){
36854 this.scale = this.startScale;
36862 onTouchEnd : function(e)
36864 this.pinching = false;
36865 this.dragable = false;
36869 process : function(file, crop)
36872 this.maskEl.mask(this.loadingText);
36875 this.xhr = new XMLHttpRequest();
36877 file.xhr = this.xhr;
36879 this.xhr.open(this.method, this.url, true);
36882 "Accept": "application/json",
36883 "Cache-Control": "no-cache",
36884 "X-Requested-With": "XMLHttpRequest"
36887 for (var headerName in headers) {
36888 var headerValue = headers[headerName];
36890 this.xhr.setRequestHeader(headerName, headerValue);
36896 this.xhr.onload = function()
36898 _this.xhrOnLoad(_this.xhr);
36901 this.xhr.onerror = function()
36903 _this.xhrOnError(_this.xhr);
36906 var formData = new FormData();
36908 formData.append('returnHTML', 'NO');
36911 formData.append('crop', crop);
36914 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36915 formData.append(this.paramName, file, file.name);
36918 if(typeof(file.filename) != 'undefined'){
36919 formData.append('filename', file.filename);
36922 if(typeof(file.mimetype) != 'undefined'){
36923 formData.append('mimetype', file.mimetype);
36926 if(this.fireEvent('arrange', this, formData) != false){
36927 this.xhr.send(formData);
36931 xhrOnLoad : function(xhr)
36934 this.maskEl.unmask();
36937 if (xhr.readyState !== 4) {
36938 this.fireEvent('exception', this, xhr);
36942 var response = Roo.decode(xhr.responseText);
36944 if(!response.success){
36945 this.fireEvent('exception', this, xhr);
36949 var response = Roo.decode(xhr.responseText);
36951 this.fireEvent('upload', this, response);
36955 xhrOnError : function()
36958 this.maskEl.unmask();
36961 Roo.log('xhr on error');
36963 var response = Roo.decode(xhr.responseText);
36969 prepare : function(file)
36972 this.maskEl.mask(this.loadingText);
36978 if(typeof(file) === 'string'){
36979 this.loadCanvas(file);
36983 if(!file || !this.urlAPI){
36988 this.cropType = file.type;
36992 if(this.fireEvent('prepare', this, this.file) != false){
36994 var reader = new FileReader();
36996 reader.onload = function (e) {
36997 if (e.target.error) {
36998 Roo.log(e.target.error);
37002 var buffer = e.target.result,
37003 dataView = new DataView(buffer),
37005 maxOffset = dataView.byteLength - 4,
37009 if (dataView.getUint16(0) === 0xffd8) {
37010 while (offset < maxOffset) {
37011 markerBytes = dataView.getUint16(offset);
37013 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37014 markerLength = dataView.getUint16(offset + 2) + 2;
37015 if (offset + markerLength > dataView.byteLength) {
37016 Roo.log('Invalid meta data: Invalid segment size.');
37020 if(markerBytes == 0xffe1){
37021 _this.parseExifData(
37028 offset += markerLength;
37038 var url = _this.urlAPI.createObjectURL(_this.file);
37040 _this.loadCanvas(url);
37045 reader.readAsArrayBuffer(this.file);
37051 parseExifData : function(dataView, offset, length)
37053 var tiffOffset = offset + 10,
37057 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37058 // No Exif data, might be XMP data instead
37062 // Check for the ASCII code for "Exif" (0x45786966):
37063 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37064 // No Exif data, might be XMP data instead
37067 if (tiffOffset + 8 > dataView.byteLength) {
37068 Roo.log('Invalid Exif data: Invalid segment size.');
37071 // Check for the two null bytes:
37072 if (dataView.getUint16(offset + 8) !== 0x0000) {
37073 Roo.log('Invalid Exif data: Missing byte alignment offset.');
37076 // Check the byte alignment:
37077 switch (dataView.getUint16(tiffOffset)) {
37079 littleEndian = true;
37082 littleEndian = false;
37085 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37088 // Check for the TIFF tag marker (0x002A):
37089 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37090 Roo.log('Invalid Exif data: Missing TIFF marker.');
37093 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37094 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37096 this.parseExifTags(
37099 tiffOffset + dirOffset,
37104 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37109 if (dirOffset + 6 > dataView.byteLength) {
37110 Roo.log('Invalid Exif data: Invalid directory offset.');
37113 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37114 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37115 if (dirEndOffset + 4 > dataView.byteLength) {
37116 Roo.log('Invalid Exif data: Invalid directory size.');
37119 for (i = 0; i < tagsNumber; i += 1) {
37123 dirOffset + 2 + 12 * i, // tag offset
37127 // Return the offset to the next directory:
37128 return dataView.getUint32(dirEndOffset, littleEndian);
37131 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
37133 var tag = dataView.getUint16(offset, littleEndian);
37135 this.exif[tag] = this.getExifValue(
37139 dataView.getUint16(offset + 2, littleEndian), // tag type
37140 dataView.getUint32(offset + 4, littleEndian), // tag length
37145 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37147 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37156 Roo.log('Invalid Exif data: Invalid tag type.');
37160 tagSize = tagType.size * length;
37161 // Determine if the value is contained in the dataOffset bytes,
37162 // or if the value at the dataOffset is a pointer to the actual data:
37163 dataOffset = tagSize > 4 ?
37164 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37165 if (dataOffset + tagSize > dataView.byteLength) {
37166 Roo.log('Invalid Exif data: Invalid data offset.');
37169 if (length === 1) {
37170 return tagType.getValue(dataView, dataOffset, littleEndian);
37173 for (i = 0; i < length; i += 1) {
37174 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37177 if (tagType.ascii) {
37179 // Concatenate the chars:
37180 for (i = 0; i < values.length; i += 1) {
37182 // Ignore the terminating NULL byte(s):
37183 if (c === '\u0000') {
37195 Roo.apply(Roo.bootstrap.UploadCropbox, {
37197 'Orientation': 0x0112
37201 1: 0, //'top-left',
37203 3: 180, //'bottom-right',
37204 // 4: 'bottom-left',
37206 6: 90, //'right-top',
37207 // 7: 'right-bottom',
37208 8: 270 //'left-bottom'
37212 // byte, 8-bit unsigned int:
37214 getValue: function (dataView, dataOffset) {
37215 return dataView.getUint8(dataOffset);
37219 // ascii, 8-bit byte:
37221 getValue: function (dataView, dataOffset) {
37222 return String.fromCharCode(dataView.getUint8(dataOffset));
37227 // short, 16 bit int:
37229 getValue: function (dataView, dataOffset, littleEndian) {
37230 return dataView.getUint16(dataOffset, littleEndian);
37234 // long, 32 bit int:
37236 getValue: function (dataView, dataOffset, littleEndian) {
37237 return dataView.getUint32(dataOffset, littleEndian);
37241 // rational = two long values, first is numerator, second is denominator:
37243 getValue: function (dataView, dataOffset, littleEndian) {
37244 return dataView.getUint32(dataOffset, littleEndian) /
37245 dataView.getUint32(dataOffset + 4, littleEndian);
37249 // slong, 32 bit signed int:
37251 getValue: function (dataView, dataOffset, littleEndian) {
37252 return dataView.getInt32(dataOffset, littleEndian);
37256 // srational, two slongs, first is numerator, second is denominator:
37258 getValue: function (dataView, dataOffset, littleEndian) {
37259 return dataView.getInt32(dataOffset, littleEndian) /
37260 dataView.getInt32(dataOffset + 4, littleEndian);
37270 cls : 'btn-group roo-upload-cropbox-rotate-left',
37271 action : 'rotate-left',
37275 cls : 'btn btn-default',
37276 html : '<i class="fa fa-undo"></i>'
37282 cls : 'btn-group roo-upload-cropbox-picture',
37283 action : 'picture',
37287 cls : 'btn btn-default',
37288 html : '<i class="fa fa-picture-o"></i>'
37294 cls : 'btn-group roo-upload-cropbox-rotate-right',
37295 action : 'rotate-right',
37299 cls : 'btn btn-default',
37300 html : '<i class="fa fa-repeat"></i>'
37308 cls : 'btn-group roo-upload-cropbox-rotate-left',
37309 action : 'rotate-left',
37313 cls : 'btn btn-default',
37314 html : '<i class="fa fa-undo"></i>'
37320 cls : 'btn-group roo-upload-cropbox-download',
37321 action : 'download',
37325 cls : 'btn btn-default',
37326 html : '<i class="fa fa-download"></i>'
37332 cls : 'btn-group roo-upload-cropbox-crop',
37337 cls : 'btn btn-default',
37338 html : '<i class="fa fa-crop"></i>'
37344 cls : 'btn-group roo-upload-cropbox-trash',
37349 cls : 'btn btn-default',
37350 html : '<i class="fa fa-trash"></i>'
37356 cls : 'btn-group roo-upload-cropbox-rotate-right',
37357 action : 'rotate-right',
37361 cls : 'btn btn-default',
37362 html : '<i class="fa fa-repeat"></i>'
37370 cls : 'btn-group roo-upload-cropbox-rotate-left',
37371 action : 'rotate-left',
37375 cls : 'btn btn-default',
37376 html : '<i class="fa fa-undo"></i>'
37382 cls : 'btn-group roo-upload-cropbox-rotate-right',
37383 action : 'rotate-right',
37387 cls : 'btn btn-default',
37388 html : '<i class="fa fa-repeat"></i>'
37401 * @class Roo.bootstrap.DocumentManager
37402 * @extends Roo.bootstrap.Component
37403 * Bootstrap DocumentManager class
37404 * @cfg {String} paramName default 'imageUpload'
37405 * @cfg {String} toolTipName default 'filename'
37406 * @cfg {String} method default POST
37407 * @cfg {String} url action url
37408 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37409 * @cfg {Boolean} multiple multiple upload default true
37410 * @cfg {Number} thumbSize default 300
37411 * @cfg {String} fieldLabel
37412 * @cfg {Number} labelWidth default 4
37413 * @cfg {String} labelAlign (left|top) default left
37414 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37415 * @cfg {Number} labellg set the width of label (1-12)
37416 * @cfg {Number} labelmd set the width of label (1-12)
37417 * @cfg {Number} labelsm set the width of label (1-12)
37418 * @cfg {Number} labelxs set the width of label (1-12)
37421 * Create a new DocumentManager
37422 * @param {Object} config The config object
37425 Roo.bootstrap.DocumentManager = function(config){
37426 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37429 this.delegates = [];
37434 * Fire when initial the DocumentManager
37435 * @param {Roo.bootstrap.DocumentManager} this
37440 * inspect selected file
37441 * @param {Roo.bootstrap.DocumentManager} this
37442 * @param {File} file
37447 * Fire when xhr load exception
37448 * @param {Roo.bootstrap.DocumentManager} this
37449 * @param {XMLHttpRequest} xhr
37451 "exception" : true,
37453 * @event afterupload
37454 * Fire when xhr load exception
37455 * @param {Roo.bootstrap.DocumentManager} this
37456 * @param {XMLHttpRequest} xhr
37458 "afterupload" : true,
37461 * prepare the form data
37462 * @param {Roo.bootstrap.DocumentManager} this
37463 * @param {Object} formData
37468 * Fire when remove the file
37469 * @param {Roo.bootstrap.DocumentManager} this
37470 * @param {Object} file
37475 * Fire after refresh the file
37476 * @param {Roo.bootstrap.DocumentManager} this
37481 * Fire after click the image
37482 * @param {Roo.bootstrap.DocumentManager} this
37483 * @param {Object} file
37488 * Fire when upload a image and editable set to true
37489 * @param {Roo.bootstrap.DocumentManager} this
37490 * @param {Object} file
37494 * @event beforeselectfile
37495 * Fire before select file
37496 * @param {Roo.bootstrap.DocumentManager} this
37498 "beforeselectfile" : true,
37501 * Fire before process file
37502 * @param {Roo.bootstrap.DocumentManager} this
37503 * @param {Object} file
37507 * @event previewrendered
37508 * Fire when preview rendered
37509 * @param {Roo.bootstrap.DocumentManager} this
37510 * @param {Object} file
37512 "previewrendered" : true,
37515 "previewResize" : true
37520 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
37529 paramName : 'imageUpload',
37530 toolTipName : 'filename',
37533 labelAlign : 'left',
37543 getAutoCreate : function()
37545 var managerWidget = {
37547 cls : 'roo-document-manager',
37551 cls : 'roo-document-manager-selector',
37556 cls : 'roo-document-manager-uploader',
37560 cls : 'roo-document-manager-upload-btn',
37561 html : '<i class="fa fa-plus"></i>'
37572 cls : 'column col-md-12',
37577 if(this.fieldLabel.length){
37582 cls : 'column col-md-12',
37583 html : this.fieldLabel
37587 cls : 'column col-md-12',
37592 if(this.labelAlign == 'left'){
37597 html : this.fieldLabel
37606 if(this.labelWidth > 12){
37607 content[0].style = "width: " + this.labelWidth + 'px';
37610 if(this.labelWidth < 13 && this.labelmd == 0){
37611 this.labelmd = this.labelWidth;
37614 if(this.labellg > 0){
37615 content[0].cls += ' col-lg-' + this.labellg;
37616 content[1].cls += ' col-lg-' + (12 - this.labellg);
37619 if(this.labelmd > 0){
37620 content[0].cls += ' col-md-' + this.labelmd;
37621 content[1].cls += ' col-md-' + (12 - this.labelmd);
37624 if(this.labelsm > 0){
37625 content[0].cls += ' col-sm-' + this.labelsm;
37626 content[1].cls += ' col-sm-' + (12 - this.labelsm);
37629 if(this.labelxs > 0){
37630 content[0].cls += ' col-xs-' + this.labelxs;
37631 content[1].cls += ' col-xs-' + (12 - this.labelxs);
37639 cls : 'row clearfix',
37647 initEvents : function()
37649 this.managerEl = this.el.select('.roo-document-manager', true).first();
37650 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37652 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37653 this.selectorEl.hide();
37656 this.selectorEl.attr('multiple', 'multiple');
37659 this.selectorEl.on('change', this.onFileSelected, this);
37661 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37662 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37664 this.uploader.on('click', this.onUploaderClick, this);
37666 this.renderProgressDialog();
37670 window.addEventListener("resize", function() { _this.refresh(); } );
37672 this.fireEvent('initial', this);
37675 renderProgressDialog : function()
37679 this.progressDialog = new Roo.bootstrap.Modal({
37680 cls : 'roo-document-manager-progress-dialog',
37681 allow_close : false,
37692 btnclick : function() {
37693 _this.uploadCancel();
37699 this.progressDialog.render(Roo.get(document.body));
37701 this.progress = new Roo.bootstrap.Progress({
37702 cls : 'roo-document-manager-progress',
37707 this.progress.render(this.progressDialog.getChildContainer());
37709 this.progressBar = new Roo.bootstrap.ProgressBar({
37710 cls : 'roo-document-manager-progress-bar',
37713 aria_valuemax : 12,
37717 this.progressBar.render(this.progress.getChildContainer());
37720 onUploaderClick : function(e)
37722 e.preventDefault();
37724 if(this.fireEvent('beforeselectfile', this) != false){
37725 this.selectorEl.dom.click();
37730 onFileSelected : function(e)
37732 e.preventDefault();
37734 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37738 Roo.each(this.selectorEl.dom.files, function(file){
37739 if(this.fireEvent('inspect', this, file) != false){
37740 this.files.push(file);
37750 this.selectorEl.dom.value = '';
37752 if(!this.files || !this.files.length){
37756 if(this.boxes > 0 && this.files.length > this.boxes){
37757 this.files = this.files.slice(0, this.boxes);
37760 this.uploader.show();
37762 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37763 this.uploader.hide();
37772 Roo.each(this.files, function(file){
37774 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37775 var f = this.renderPreview(file);
37780 if(file.type.indexOf('image') != -1){
37781 this.delegates.push(
37783 _this.process(file);
37784 }).createDelegate(this)
37792 _this.process(file);
37793 }).createDelegate(this)
37798 this.files = files;
37800 this.delegates = this.delegates.concat(docs);
37802 if(!this.delegates.length){
37807 this.progressBar.aria_valuemax = this.delegates.length;
37814 arrange : function()
37816 if(!this.delegates.length){
37817 this.progressDialog.hide();
37822 var delegate = this.delegates.shift();
37824 this.progressDialog.show();
37826 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37828 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37833 refresh : function()
37835 this.uploader.show();
37837 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37838 this.uploader.hide();
37841 Roo.isTouch ? this.closable(false) : this.closable(true);
37843 this.fireEvent('refresh', this);
37846 onRemove : function(e, el, o)
37848 e.preventDefault();
37850 this.fireEvent('remove', this, o);
37854 remove : function(o)
37858 Roo.each(this.files, function(file){
37859 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37868 this.files = files;
37875 Roo.each(this.files, function(file){
37880 file.target.remove();
37889 onClick : function(e, el, o)
37891 e.preventDefault();
37893 this.fireEvent('click', this, o);
37897 closable : function(closable)
37899 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37901 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37913 xhrOnLoad : function(xhr)
37915 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37919 if (xhr.readyState !== 4) {
37921 this.fireEvent('exception', this, xhr);
37925 var response = Roo.decode(xhr.responseText);
37927 if(!response.success){
37929 this.fireEvent('exception', this, xhr);
37933 var file = this.renderPreview(response.data);
37935 this.files.push(file);
37939 this.fireEvent('afterupload', this, xhr);
37943 xhrOnError : function(xhr)
37945 Roo.log('xhr on error');
37947 var response = Roo.decode(xhr.responseText);
37954 process : function(file)
37956 if(this.fireEvent('process', this, file) !== false){
37957 if(this.editable && file.type.indexOf('image') != -1){
37958 this.fireEvent('edit', this, file);
37962 this.uploadStart(file, false);
37969 uploadStart : function(file, crop)
37971 this.xhr = new XMLHttpRequest();
37973 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37978 file.xhr = this.xhr;
37980 this.managerEl.createChild({
37982 cls : 'roo-document-manager-loading',
37986 tooltip : file.name,
37987 cls : 'roo-document-manager-thumb',
37988 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37994 this.xhr.open(this.method, this.url, true);
37997 "Accept": "application/json",
37998 "Cache-Control": "no-cache",
37999 "X-Requested-With": "XMLHttpRequest"
38002 for (var headerName in headers) {
38003 var headerValue = headers[headerName];
38005 this.xhr.setRequestHeader(headerName, headerValue);
38011 this.xhr.onload = function()
38013 _this.xhrOnLoad(_this.xhr);
38016 this.xhr.onerror = function()
38018 _this.xhrOnError(_this.xhr);
38021 var formData = new FormData();
38023 formData.append('returnHTML', 'NO');
38026 formData.append('crop', crop);
38029 formData.append(this.paramName, file, file.name);
38036 if(this.fireEvent('prepare', this, formData, options) != false){
38038 if(options.manually){
38042 this.xhr.send(formData);
38046 this.uploadCancel();
38049 uploadCancel : function()
38055 this.delegates = [];
38057 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38064 renderPreview : function(file)
38066 if(typeof(file.target) != 'undefined' && file.target){
38070 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38072 var previewEl = this.managerEl.createChild({
38074 cls : 'roo-document-manager-preview',
38078 tooltip : file[this.toolTipName],
38079 cls : 'roo-document-manager-thumb',
38080 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38085 html : '<i class="fa fa-times-circle"></i>'
38090 var close = previewEl.select('button.close', true).first();
38092 close.on('click', this.onRemove, this, file);
38094 file.target = previewEl;
38096 var image = previewEl.select('img', true).first();
38100 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38102 image.on('click', this.onClick, this, file);
38104 this.fireEvent('previewrendered', this, file);
38110 onPreviewLoad : function(file, image)
38112 if(typeof(file.target) == 'undefined' || !file.target){
38116 var width = image.dom.naturalWidth || image.dom.width;
38117 var height = image.dom.naturalHeight || image.dom.height;
38119 if(!this.previewResize) {
38123 if(width > height){
38124 file.target.addClass('wide');
38128 file.target.addClass('tall');
38133 uploadFromSource : function(file, crop)
38135 this.xhr = new XMLHttpRequest();
38137 this.managerEl.createChild({
38139 cls : 'roo-document-manager-loading',
38143 tooltip : file.name,
38144 cls : 'roo-document-manager-thumb',
38145 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38151 this.xhr.open(this.method, this.url, true);
38154 "Accept": "application/json",
38155 "Cache-Control": "no-cache",
38156 "X-Requested-With": "XMLHttpRequest"
38159 for (var headerName in headers) {
38160 var headerValue = headers[headerName];
38162 this.xhr.setRequestHeader(headerName, headerValue);
38168 this.xhr.onload = function()
38170 _this.xhrOnLoad(_this.xhr);
38173 this.xhr.onerror = function()
38175 _this.xhrOnError(_this.xhr);
38178 var formData = new FormData();
38180 formData.append('returnHTML', 'NO');
38182 formData.append('crop', crop);
38184 if(typeof(file.filename) != 'undefined'){
38185 formData.append('filename', file.filename);
38188 if(typeof(file.mimetype) != 'undefined'){
38189 formData.append('mimetype', file.mimetype);
38194 if(this.fireEvent('prepare', this, formData) != false){
38195 this.xhr.send(formData);
38205 * @class Roo.bootstrap.DocumentViewer
38206 * @extends Roo.bootstrap.Component
38207 * Bootstrap DocumentViewer class
38208 * @cfg {Boolean} showDownload (true|false) show download button (default true)
38209 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38212 * Create a new DocumentViewer
38213 * @param {Object} config The config object
38216 Roo.bootstrap.DocumentViewer = function(config){
38217 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38222 * Fire after initEvent
38223 * @param {Roo.bootstrap.DocumentViewer} this
38229 * @param {Roo.bootstrap.DocumentViewer} this
38234 * Fire after download button
38235 * @param {Roo.bootstrap.DocumentViewer} this
38240 * Fire after trash button
38241 * @param {Roo.bootstrap.DocumentViewer} this
38248 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
38250 showDownload : true,
38254 getAutoCreate : function()
38258 cls : 'roo-document-viewer',
38262 cls : 'roo-document-viewer-body',
38266 cls : 'roo-document-viewer-thumb',
38270 cls : 'roo-document-viewer-image'
38278 cls : 'roo-document-viewer-footer',
38281 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38285 cls : 'btn-group roo-document-viewer-download',
38289 cls : 'btn btn-default',
38290 html : '<i class="fa fa-download"></i>'
38296 cls : 'btn-group roo-document-viewer-trash',
38300 cls : 'btn btn-default',
38301 html : '<i class="fa fa-trash"></i>'
38314 initEvents : function()
38316 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38317 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38319 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38320 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38322 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38323 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38325 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38326 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38328 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38329 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38331 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38332 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38334 this.bodyEl.on('click', this.onClick, this);
38335 this.downloadBtn.on('click', this.onDownload, this);
38336 this.trashBtn.on('click', this.onTrash, this);
38338 this.downloadBtn.hide();
38339 this.trashBtn.hide();
38341 if(this.showDownload){
38342 this.downloadBtn.show();
38345 if(this.showTrash){
38346 this.trashBtn.show();
38349 if(!this.showDownload && !this.showTrash) {
38350 this.footerEl.hide();
38355 initial : function()
38357 this.fireEvent('initial', this);
38361 onClick : function(e)
38363 e.preventDefault();
38365 this.fireEvent('click', this);
38368 onDownload : function(e)
38370 e.preventDefault();
38372 this.fireEvent('download', this);
38375 onTrash : function(e)
38377 e.preventDefault();
38379 this.fireEvent('trash', this);
38391 * @class Roo.bootstrap.form.FieldLabel
38392 * @extends Roo.bootstrap.Component
38393 * Bootstrap FieldLabel class
38394 * @cfg {String} html contents of the element
38395 * @cfg {String} tag tag of the element default label
38396 * @cfg {String} cls class of the element
38397 * @cfg {String} target label target
38398 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38399 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38400 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38401 * @cfg {String} iconTooltip default "This field is required"
38402 * @cfg {String} indicatorpos (left|right) default left
38405 * Create a new FieldLabel
38406 * @param {Object} config The config object
38409 Roo.bootstrap.form.FieldLabel = function(config){
38410 Roo.bootstrap.Element.superclass.constructor.call(this, config);
38415 * Fires after the field has been marked as invalid.
38416 * @param {Roo.form.FieldLabel} this
38417 * @param {String} msg The validation message
38422 * Fires after the field has been validated with no errors.
38423 * @param {Roo.form.FieldLabel} this
38429 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
38436 invalidClass : 'has-warning',
38437 validClass : 'has-success',
38438 iconTooltip : 'This field is required',
38439 indicatorpos : 'left',
38441 getAutoCreate : function(){
38444 if (!this.allowBlank) {
38450 cls : 'roo-bootstrap-field-label ' + this.cls,
38455 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38456 tooltip : this.iconTooltip
38465 if(this.indicatorpos == 'right'){
38468 cls : 'roo-bootstrap-field-label ' + this.cls,
38477 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38478 tooltip : this.iconTooltip
38487 initEvents: function()
38489 Roo.bootstrap.Element.superclass.initEvents.call(this);
38491 this.indicator = this.indicatorEl();
38493 if(this.indicator){
38494 this.indicator.removeClass('visible');
38495 this.indicator.addClass('invisible');
38498 Roo.bootstrap.form.FieldLabel.register(this);
38501 indicatorEl : function()
38503 var indicator = this.el.select('i.roo-required-indicator',true).first();
38514 * Mark this field as valid
38516 markValid : function()
38518 if(this.indicator){
38519 this.indicator.removeClass('visible');
38520 this.indicator.addClass('invisible');
38522 if (Roo.bootstrap.version == 3) {
38523 this.el.removeClass(this.invalidClass);
38524 this.el.addClass(this.validClass);
38526 this.el.removeClass('is-invalid');
38527 this.el.addClass('is-valid');
38531 this.fireEvent('valid', this);
38535 * Mark this field as invalid
38536 * @param {String} msg The validation message
38538 markInvalid : function(msg)
38540 if(this.indicator){
38541 this.indicator.removeClass('invisible');
38542 this.indicator.addClass('visible');
38544 if (Roo.bootstrap.version == 3) {
38545 this.el.removeClass(this.validClass);
38546 this.el.addClass(this.invalidClass);
38548 this.el.removeClass('is-valid');
38549 this.el.addClass('is-invalid');
38553 this.fireEvent('invalid', this, msg);
38559 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38564 * register a FieldLabel Group
38565 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38567 register : function(label)
38569 if(this.groups.hasOwnProperty(label.target)){
38573 this.groups[label.target] = label;
38577 * fetch a FieldLabel Group based on the target
38578 * @param {string} target
38579 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38581 get: function(target) {
38582 if (typeof(this.groups[target]) == 'undefined') {
38586 return this.groups[target] ;
38595 * page DateSplitField.
38601 * @class Roo.bootstrap.form.DateSplitField
38602 * @extends Roo.bootstrap.Component
38603 * Bootstrap DateSplitField class
38604 * @cfg {string} fieldLabel - the label associated
38605 * @cfg {Number} labelWidth set the width of label (0-12)
38606 * @cfg {String} labelAlign (top|left)
38607 * @cfg {Boolean} dayAllowBlank (true|false) default false
38608 * @cfg {Boolean} monthAllowBlank (true|false) default false
38609 * @cfg {Boolean} yearAllowBlank (true|false) default false
38610 * @cfg {string} dayPlaceholder
38611 * @cfg {string} monthPlaceholder
38612 * @cfg {string} yearPlaceholder
38613 * @cfg {string} dayFormat default 'd'
38614 * @cfg {string} monthFormat default 'm'
38615 * @cfg {string} yearFormat default 'Y'
38616 * @cfg {Number} labellg set the width of label (1-12)
38617 * @cfg {Number} labelmd set the width of label (1-12)
38618 * @cfg {Number} labelsm set the width of label (1-12)
38619 * @cfg {Number} labelxs set the width of label (1-12)
38623 * Create a new DateSplitField
38624 * @param {Object} config The config object
38627 Roo.bootstrap.form.DateSplitField = function(config){
38628 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38634 * getting the data of years
38635 * @param {Roo.bootstrap.form.DateSplitField} this
38636 * @param {Object} years
38641 * getting the data of days
38642 * @param {Roo.bootstrap.form.DateSplitField} this
38643 * @param {Object} days
38648 * Fires after the field has been marked as invalid.
38649 * @param {Roo.form.Field} this
38650 * @param {String} msg The validation message
38655 * Fires after the field has been validated with no errors.
38656 * @param {Roo.form.Field} this
38662 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
38665 labelAlign : 'top',
38667 dayAllowBlank : false,
38668 monthAllowBlank : false,
38669 yearAllowBlank : false,
38670 dayPlaceholder : '',
38671 monthPlaceholder : '',
38672 yearPlaceholder : '',
38676 isFormField : true,
38682 getAutoCreate : function()
38686 cls : 'row roo-date-split-field-group',
38691 cls : 'form-hidden-field roo-date-split-field-group-value',
38697 var labelCls = 'col-md-12';
38698 var contentCls = 'col-md-4';
38700 if(this.fieldLabel){
38704 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38708 html : this.fieldLabel
38713 if(this.labelAlign == 'left'){
38715 if(this.labelWidth > 12){
38716 label.style = "width: " + this.labelWidth + 'px';
38719 if(this.labelWidth < 13 && this.labelmd == 0){
38720 this.labelmd = this.labelWidth;
38723 if(this.labellg > 0){
38724 labelCls = ' col-lg-' + this.labellg;
38725 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38728 if(this.labelmd > 0){
38729 labelCls = ' col-md-' + this.labelmd;
38730 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38733 if(this.labelsm > 0){
38734 labelCls = ' col-sm-' + this.labelsm;
38735 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38738 if(this.labelxs > 0){
38739 labelCls = ' col-xs-' + this.labelxs;
38740 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38744 label.cls += ' ' + labelCls;
38746 cfg.cn.push(label);
38749 Roo.each(['day', 'month', 'year'], function(t){
38752 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38759 inputEl: function ()
38761 return this.el.select('.roo-date-split-field-group-value', true).first();
38764 onRender : function(ct, position)
38768 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38770 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38772 this.dayField = new Roo.bootstrap.form.ComboBox({
38773 allowBlank : this.dayAllowBlank,
38774 alwaysQuery : true,
38775 displayField : 'value',
38778 forceSelection : true,
38780 placeholder : this.dayPlaceholder,
38781 selectOnFocus : true,
38782 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38783 triggerAction : 'all',
38785 valueField : 'value',
38786 store : new Roo.data.SimpleStore({
38787 data : (function() {
38789 _this.fireEvent('days', _this, days);
38792 fields : [ 'value' ]
38795 select : function (_self, record, index)
38797 _this.setValue(_this.getValue());
38802 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38804 this.monthField = new Roo.bootstrap.form.MonthField({
38805 after : '<i class=\"fa fa-calendar\"></i>',
38806 allowBlank : this.monthAllowBlank,
38807 placeholder : this.monthPlaceholder,
38810 render : function (_self)
38812 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38813 e.preventDefault();
38817 select : function (_self, oldvalue, newvalue)
38819 _this.setValue(_this.getValue());
38824 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38826 this.yearField = new Roo.bootstrap.form.ComboBox({
38827 allowBlank : this.yearAllowBlank,
38828 alwaysQuery : true,
38829 displayField : 'value',
38832 forceSelection : true,
38834 placeholder : this.yearPlaceholder,
38835 selectOnFocus : true,
38836 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38837 triggerAction : 'all',
38839 valueField : 'value',
38840 store : new Roo.data.SimpleStore({
38841 data : (function() {
38843 _this.fireEvent('years', _this, years);
38846 fields : [ 'value' ]
38849 select : function (_self, record, index)
38851 _this.setValue(_this.getValue());
38856 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38859 setValue : function(v, format)
38861 this.inputEl.dom.value = v;
38863 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38865 var d = Date.parseDate(v, f);
38872 this.setDay(d.format(this.dayFormat));
38873 this.setMonth(d.format(this.monthFormat));
38874 this.setYear(d.format(this.yearFormat));
38881 setDay : function(v)
38883 this.dayField.setValue(v);
38884 this.inputEl.dom.value = this.getValue();
38889 setMonth : function(v)
38891 this.monthField.setValue(v, true);
38892 this.inputEl.dom.value = this.getValue();
38897 setYear : function(v)
38899 this.yearField.setValue(v);
38900 this.inputEl.dom.value = this.getValue();
38905 getDay : function()
38907 return this.dayField.getValue();
38910 getMonth : function()
38912 return this.monthField.getValue();
38915 getYear : function()
38917 return this.yearField.getValue();
38920 getValue : function()
38922 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38924 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38934 this.inputEl.dom.value = '';
38939 validate : function()
38941 var d = this.dayField.validate();
38942 var m = this.monthField.validate();
38943 var y = this.yearField.validate();
38948 (!this.dayAllowBlank && !d) ||
38949 (!this.monthAllowBlank && !m) ||
38950 (!this.yearAllowBlank && !y)
38955 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38964 this.markInvalid();
38969 markValid : function()
38972 var label = this.el.select('label', true).first();
38973 var icon = this.el.select('i.fa-star', true).first();
38979 this.fireEvent('valid', this);
38983 * Mark this field as invalid
38984 * @param {String} msg The validation message
38986 markInvalid : function(msg)
38989 var label = this.el.select('label', true).first();
38990 var icon = this.el.select('i.fa-star', true).first();
38992 if(label && !icon){
38993 this.el.select('.roo-date-split-field-label', true).createChild({
38995 cls : 'text-danger fa fa-lg fa-star',
38996 tooltip : 'This field is required',
38997 style : 'margin-right:5px;'
39001 this.fireEvent('invalid', this, msg);
39004 clearInvalid : function()
39006 var label = this.el.select('label', true).first();
39007 var icon = this.el.select('i.fa-star', true).first();
39013 this.fireEvent('valid', this);
39016 getName: function()
39026 * @class Roo.bootstrap.LayoutMasonry
39027 * @extends Roo.bootstrap.Component
39028 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39029 * Bootstrap Layout Masonry class
39032 * http://masonry.desandro.com
39034 * The idea is to render all the bricks based on vertical width...
39036 * The original code extends 'outlayer' - we might need to use that....
39039 * Create a new Element
39040 * @param {Object} config The config object
39043 Roo.bootstrap.LayoutMasonry = function(config){
39045 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39049 Roo.bootstrap.LayoutMasonry.register(this);
39055 * Fire after layout the items
39056 * @param {Roo.bootstrap.LayoutMasonry} this
39057 * @param {Roo.EventObject} e
39064 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
39067 * @cfg {Boolean} isLayoutInstant = no animation?
39069 isLayoutInstant : false, // needed?
39072 * @cfg {Number} boxWidth width of the columns
39077 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
39082 * @cfg {Number} padWidth padding below box..
39087 * @cfg {Number} gutter gutter width..
39092 * @cfg {Number} maxCols maximum number of columns
39098 * @cfg {Boolean} isAutoInitial defalut true
39100 isAutoInitial : true,
39105 * @cfg {Boolean} isHorizontal defalut false
39107 isHorizontal : false,
39109 currentSize : null,
39115 bricks: null, //CompositeElement
39119 _isLayoutInited : false,
39121 // isAlternative : false, // only use for vertical layout...
39124 * @cfg {Number} alternativePadWidth padding below box..
39126 alternativePadWidth : 50,
39128 selectedBrick : [],
39130 getAutoCreate : function(){
39132 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39136 cls: 'blog-masonary-wrapper ' + this.cls,
39138 cls : 'mas-boxes masonary'
39145 getChildContainer: function( )
39147 if (this.boxesEl) {
39148 return this.boxesEl;
39151 this.boxesEl = this.el.select('.mas-boxes').first();
39153 return this.boxesEl;
39157 initEvents : function()
39161 if(this.isAutoInitial){
39162 Roo.log('hook children rendered');
39163 this.on('childrenrendered', function() {
39164 Roo.log('children rendered');
39170 initial : function()
39172 this.selectedBrick = [];
39174 this.currentSize = this.el.getBox(true);
39176 Roo.EventManager.onWindowResize(this.resize, this);
39178 if(!this.isAutoInitial){
39186 //this.layout.defer(500,this);
39190 resize : function()
39192 var cs = this.el.getBox(true);
39195 this.currentSize.width == cs.width &&
39196 this.currentSize.x == cs.x &&
39197 this.currentSize.height == cs.height &&
39198 this.currentSize.y == cs.y
39200 Roo.log("no change in with or X or Y");
39204 this.currentSize = cs;
39210 layout : function()
39212 this._resetLayout();
39214 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39216 this.layoutItems( isInstant );
39218 this._isLayoutInited = true;
39220 this.fireEvent('layout', this);
39224 _resetLayout : function()
39226 if(this.isHorizontal){
39227 this.horizontalMeasureColumns();
39231 this.verticalMeasureColumns();
39235 verticalMeasureColumns : function()
39237 this.getContainerWidth();
39239 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39240 // this.colWidth = Math.floor(this.containerWidth * 0.8);
39244 var boxWidth = this.boxWidth + this.padWidth;
39246 if(this.containerWidth < this.boxWidth){
39247 boxWidth = this.containerWidth
39250 var containerWidth = this.containerWidth;
39252 var cols = Math.floor(containerWidth / boxWidth);
39254 this.cols = Math.max( cols, 1 );
39256 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39258 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39260 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39262 this.colWidth = boxWidth + avail - this.padWidth;
39264 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39265 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
39268 horizontalMeasureColumns : function()
39270 this.getContainerWidth();
39272 var boxWidth = this.boxWidth;
39274 if(this.containerWidth < boxWidth){
39275 boxWidth = this.containerWidth;
39278 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39280 this.el.setHeight(boxWidth);
39284 getContainerWidth : function()
39286 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
39289 layoutItems : function( isInstant )
39291 Roo.log(this.bricks);
39293 var items = Roo.apply([], this.bricks);
39295 if(this.isHorizontal){
39296 this._horizontalLayoutItems( items , isInstant );
39300 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39301 // this._verticalAlternativeLayoutItems( items , isInstant );
39305 this._verticalLayoutItems( items , isInstant );
39309 _verticalLayoutItems : function ( items , isInstant)
39311 if ( !items || !items.length ) {
39316 ['xs', 'xs', 'xs', 'tall'],
39317 ['xs', 'xs', 'tall'],
39318 ['xs', 'xs', 'sm'],
39319 ['xs', 'xs', 'xs'],
39325 ['sm', 'xs', 'xs'],
39329 ['tall', 'xs', 'xs', 'xs'],
39330 ['tall', 'xs', 'xs'],
39342 Roo.each(items, function(item, k){
39344 switch (item.size) {
39345 // these layouts take up a full box,
39356 boxes.push([item]);
39379 var filterPattern = function(box, length)
39387 var pattern = box.slice(0, length);
39391 Roo.each(pattern, function(i){
39392 format.push(i.size);
39395 Roo.each(standard, function(s){
39397 if(String(s) != String(format)){
39406 if(!match && length == 1){
39411 filterPattern(box, length - 1);
39415 queue.push(pattern);
39417 box = box.slice(length, box.length);
39419 filterPattern(box, 4);
39425 Roo.each(boxes, function(box, k){
39431 if(box.length == 1){
39436 filterPattern(box, 4);
39440 this._processVerticalLayoutQueue( queue, isInstant );
39444 // _verticalAlternativeLayoutItems : function( items , isInstant )
39446 // if ( !items || !items.length ) {
39450 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
39454 _horizontalLayoutItems : function ( items , isInstant)
39456 if ( !items || !items.length || items.length < 3) {
39462 var eItems = items.slice(0, 3);
39464 items = items.slice(3, items.length);
39467 ['xs', 'xs', 'xs', 'wide'],
39468 ['xs', 'xs', 'wide'],
39469 ['xs', 'xs', 'sm'],
39470 ['xs', 'xs', 'xs'],
39476 ['sm', 'xs', 'xs'],
39480 ['wide', 'xs', 'xs', 'xs'],
39481 ['wide', 'xs', 'xs'],
39494 Roo.each(items, function(item, k){
39496 switch (item.size) {
39507 boxes.push([item]);
39531 var filterPattern = function(box, length)
39539 var pattern = box.slice(0, length);
39543 Roo.each(pattern, function(i){
39544 format.push(i.size);
39547 Roo.each(standard, function(s){
39549 if(String(s) != String(format)){
39558 if(!match && length == 1){
39563 filterPattern(box, length - 1);
39567 queue.push(pattern);
39569 box = box.slice(length, box.length);
39571 filterPattern(box, 4);
39577 Roo.each(boxes, function(box, k){
39583 if(box.length == 1){
39588 filterPattern(box, 4);
39595 var pos = this.el.getBox(true);
39599 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39601 var hit_end = false;
39603 Roo.each(queue, function(box){
39607 Roo.each(box, function(b){
39609 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39619 Roo.each(box, function(b){
39621 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39624 mx = Math.max(mx, b.x);
39628 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39632 Roo.each(box, function(b){
39634 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39648 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39651 /** Sets position of item in DOM
39652 * @param {Element} item
39653 * @param {Number} x - horizontal position
39654 * @param {Number} y - vertical position
39655 * @param {Boolean} isInstant - disables transitions
39657 _processVerticalLayoutQueue : function( queue, isInstant )
39659 var pos = this.el.getBox(true);
39664 for (var i = 0; i < this.cols; i++){
39668 Roo.each(queue, function(box, k){
39670 var col = k % this.cols;
39672 Roo.each(box, function(b,kk){
39674 b.el.position('absolute');
39676 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39677 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39679 if(b.size == 'md-left' || b.size == 'md-right'){
39680 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39681 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39684 b.el.setWidth(width);
39685 b.el.setHeight(height);
39687 b.el.select('iframe',true).setSize(width,height);
39691 for (var i = 0; i < this.cols; i++){
39693 if(maxY[i] < maxY[col]){
39698 col = Math.min(col, i);
39702 x = pos.x + col * (this.colWidth + this.padWidth);
39706 var positions = [];
39708 switch (box.length){
39710 positions = this.getVerticalOneBoxColPositions(x, y, box);
39713 positions = this.getVerticalTwoBoxColPositions(x, y, box);
39716 positions = this.getVerticalThreeBoxColPositions(x, y, box);
39719 positions = this.getVerticalFourBoxColPositions(x, y, box);
39725 Roo.each(box, function(b,kk){
39727 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39729 var sz = b.el.getSize();
39731 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39739 for (var i = 0; i < this.cols; i++){
39740 mY = Math.max(mY, maxY[i]);
39743 this.el.setHeight(mY - pos.y);
39747 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39749 // var pos = this.el.getBox(true);
39752 // var maxX = pos.right;
39754 // var maxHeight = 0;
39756 // Roo.each(items, function(item, k){
39760 // item.el.position('absolute');
39762 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39764 // item.el.setWidth(width);
39766 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39768 // item.el.setHeight(height);
39771 // item.el.setXY([x, y], isInstant ? false : true);
39773 // item.el.setXY([maxX - width, y], isInstant ? false : true);
39776 // y = y + height + this.alternativePadWidth;
39778 // maxHeight = maxHeight + height + this.alternativePadWidth;
39782 // this.el.setHeight(maxHeight);
39786 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39788 var pos = this.el.getBox(true);
39793 var maxX = pos.right;
39795 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39797 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39799 Roo.each(queue, function(box, k){
39801 Roo.each(box, function(b, kk){
39803 b.el.position('absolute');
39805 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39806 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39808 if(b.size == 'md-left' || b.size == 'md-right'){
39809 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39810 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39813 b.el.setWidth(width);
39814 b.el.setHeight(height);
39822 var positions = [];
39824 switch (box.length){
39826 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39829 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39832 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39835 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39841 Roo.each(box, function(b,kk){
39843 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39845 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39853 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39855 Roo.each(eItems, function(b,k){
39857 b.size = (k == 0) ? 'sm' : 'xs';
39858 b.x = (k == 0) ? 2 : 1;
39859 b.y = (k == 0) ? 2 : 1;
39861 b.el.position('absolute');
39863 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39865 b.el.setWidth(width);
39867 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39869 b.el.setHeight(height);
39873 var positions = [];
39876 x : maxX - this.unitWidth * 2 - this.gutter,
39881 x : maxX - this.unitWidth,
39882 y : minY + (this.unitWidth + this.gutter) * 2
39886 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39890 Roo.each(eItems, function(b,k){
39892 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39898 getVerticalOneBoxColPositions : function(x, y, box)
39902 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39904 if(box[0].size == 'md-left'){
39908 if(box[0].size == 'md-right'){
39913 x : x + (this.unitWidth + this.gutter) * rand,
39920 getVerticalTwoBoxColPositions : function(x, y, box)
39924 if(box[0].size == 'xs'){
39928 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39932 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39946 x : x + (this.unitWidth + this.gutter) * 2,
39947 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39954 getVerticalThreeBoxColPositions : function(x, y, box)
39958 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39966 x : x + (this.unitWidth + this.gutter) * 1,
39971 x : x + (this.unitWidth + this.gutter) * 2,
39979 if(box[0].size == 'xs' && box[1].size == 'xs'){
39988 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39992 x : x + (this.unitWidth + this.gutter) * 1,
40006 x : x + (this.unitWidth + this.gutter) * 2,
40011 x : x + (this.unitWidth + this.gutter) * 2,
40012 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40019 getVerticalFourBoxColPositions : function(x, y, box)
40023 if(box[0].size == 'xs'){
40032 y : y + (this.unitHeight + this.gutter) * 1
40037 y : y + (this.unitHeight + this.gutter) * 2
40041 x : x + (this.unitWidth + this.gutter) * 1,
40055 x : x + (this.unitWidth + this.gutter) * 2,
40060 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40061 y : y + (this.unitHeight + this.gutter) * 1
40065 x : x + (this.unitWidth + this.gutter) * 2,
40066 y : y + (this.unitWidth + this.gutter) * 2
40073 getHorizontalOneBoxColPositions : function(maxX, minY, box)
40077 if(box[0].size == 'md-left'){
40079 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40086 if(box[0].size == 'md-right'){
40088 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40089 y : minY + (this.unitWidth + this.gutter) * 1
40095 var rand = Math.floor(Math.random() * (4 - box[0].y));
40098 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40099 y : minY + (this.unitWidth + this.gutter) * rand
40106 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40110 if(box[0].size == 'xs'){
40113 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40118 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40119 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40127 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40132 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40133 y : minY + (this.unitWidth + this.gutter) * 2
40140 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40144 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40147 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40152 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40153 y : minY + (this.unitWidth + this.gutter) * 1
40157 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40158 y : minY + (this.unitWidth + this.gutter) * 2
40165 if(box[0].size == 'xs' && box[1].size == 'xs'){
40168 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40173 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40178 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40179 y : minY + (this.unitWidth + this.gutter) * 1
40187 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40192 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40193 y : minY + (this.unitWidth + this.gutter) * 2
40197 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40198 y : minY + (this.unitWidth + this.gutter) * 2
40205 getHorizontalFourBoxColPositions : function(maxX, minY, box)
40209 if(box[0].size == 'xs'){
40212 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40217 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40222 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),
40227 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40228 y : minY + (this.unitWidth + this.gutter) * 1
40236 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40241 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40242 y : minY + (this.unitWidth + this.gutter) * 2
40246 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40247 y : minY + (this.unitWidth + this.gutter) * 2
40251 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),
40252 y : minY + (this.unitWidth + this.gutter) * 2
40260 * remove a Masonry Brick
40261 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40263 removeBrick : function(brick_id)
40269 for (var i = 0; i<this.bricks.length; i++) {
40270 if (this.bricks[i].id == brick_id) {
40271 this.bricks.splice(i,1);
40272 this.el.dom.removeChild(Roo.get(brick_id).dom);
40279 * adds a Masonry Brick
40280 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40282 addBrick : function(cfg)
40284 var cn = new Roo.bootstrap.MasonryBrick(cfg);
40285 //this.register(cn);
40286 cn.parentId = this.id;
40287 cn.render(this.el);
40292 * register a Masonry Brick
40293 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40296 register : function(brick)
40298 this.bricks.push(brick);
40299 brick.masonryId = this.id;
40303 * clear all the Masonry Brick
40305 clearAll : function()
40308 //this.getChildContainer().dom.innerHTML = "";
40309 this.el.dom.innerHTML = '';
40312 getSelected : function()
40314 if (!this.selectedBrick) {
40318 return this.selectedBrick;
40322 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40326 * register a Masonry Layout
40327 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40330 register : function(layout)
40332 this.groups[layout.id] = layout;
40335 * fetch a Masonry Layout based on the masonry layout ID
40336 * @param {string} the masonry layout to add
40337 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40340 get: function(layout_id) {
40341 if (typeof(this.groups[layout_id]) == 'undefined') {
40344 return this.groups[layout_id] ;
40356 * http://masonry.desandro.com
40358 * The idea is to render all the bricks based on vertical width...
40360 * The original code extends 'outlayer' - we might need to use that....
40366 * @class Roo.bootstrap.LayoutMasonryAuto
40367 * @extends Roo.bootstrap.Component
40368 * Bootstrap Layout Masonry class
40371 * Create a new Element
40372 * @param {Object} config The config object
40375 Roo.bootstrap.LayoutMasonryAuto = function(config){
40376 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40379 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
40382 * @cfg {Boolean} isFitWidth - resize the width..
40384 isFitWidth : false, // options..
40386 * @cfg {Boolean} isOriginLeft = left align?
40388 isOriginLeft : true,
40390 * @cfg {Boolean} isOriginTop = top align?
40392 isOriginTop : false,
40394 * @cfg {Boolean} isLayoutInstant = no animation?
40396 isLayoutInstant : false, // needed?
40398 * @cfg {Boolean} isResizingContainer = not sure if this is used..
40400 isResizingContainer : true,
40402 * @cfg {Number} columnWidth width of the columns
40408 * @cfg {Number} maxCols maximum number of columns
40413 * @cfg {Number} padHeight padding below box..
40419 * @cfg {Boolean} isAutoInitial defalut true
40422 isAutoInitial : true,
40428 initialColumnWidth : 0,
40429 currentSize : null,
40431 colYs : null, // array.
40438 bricks: null, //CompositeElement
40439 cols : 0, // array?
40440 // element : null, // wrapped now this.el
40441 _isLayoutInited : null,
40444 getAutoCreate : function(){
40448 cls: 'blog-masonary-wrapper ' + this.cls,
40450 cls : 'mas-boxes masonary'
40457 getChildContainer: function( )
40459 if (this.boxesEl) {
40460 return this.boxesEl;
40463 this.boxesEl = this.el.select('.mas-boxes').first();
40465 return this.boxesEl;
40469 initEvents : function()
40473 if(this.isAutoInitial){
40474 Roo.log('hook children rendered');
40475 this.on('childrenrendered', function() {
40476 Roo.log('children rendered');
40483 initial : function()
40485 this.reloadItems();
40487 this.currentSize = this.el.getBox(true);
40489 /// was window resize... - let's see if this works..
40490 Roo.EventManager.onWindowResize(this.resize, this);
40492 if(!this.isAutoInitial){
40497 this.layout.defer(500,this);
40500 reloadItems: function()
40502 this.bricks = this.el.select('.masonry-brick', true);
40504 this.bricks.each(function(b) {
40505 //Roo.log(b.getSize());
40506 if (!b.attr('originalwidth')) {
40507 b.attr('originalwidth', b.getSize().width);
40512 Roo.log(this.bricks.elements.length);
40515 resize : function()
40518 var cs = this.el.getBox(true);
40520 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40521 Roo.log("no change in with or X");
40524 this.currentSize = cs;
40528 layout : function()
40531 this._resetLayout();
40532 //this._manageStamps();
40534 // don't animate first layout
40535 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40536 this.layoutItems( isInstant );
40538 // flag for initalized
40539 this._isLayoutInited = true;
40542 layoutItems : function( isInstant )
40544 //var items = this._getItemsForLayout( this.items );
40545 // original code supports filtering layout items.. we just ignore it..
40547 this._layoutItems( this.bricks , isInstant );
40549 this._postLayout();
40551 _layoutItems : function ( items , isInstant)
40553 //this.fireEvent( 'layout', this, items );
40556 if ( !items || !items.elements.length ) {
40557 // no items, emit event with empty array
40562 items.each(function(item) {
40563 Roo.log("layout item");
40565 // get x/y object from method
40566 var position = this._getItemLayoutPosition( item );
40568 position.item = item;
40569 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40570 queue.push( position );
40573 this._processLayoutQueue( queue );
40575 /** Sets position of item in DOM
40576 * @param {Element} item
40577 * @param {Number} x - horizontal position
40578 * @param {Number} y - vertical position
40579 * @param {Boolean} isInstant - disables transitions
40581 _processLayoutQueue : function( queue )
40583 for ( var i=0, len = queue.length; i < len; i++ ) {
40584 var obj = queue[i];
40585 obj.item.position('absolute');
40586 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40592 * Any logic you want to do after each layout,
40593 * i.e. size the container
40595 _postLayout : function()
40597 this.resizeContainer();
40600 resizeContainer : function()
40602 if ( !this.isResizingContainer ) {
40605 var size = this._getContainerSize();
40607 this.el.setSize(size.width,size.height);
40608 this.boxesEl.setSize(size.width,size.height);
40614 _resetLayout : function()
40616 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40617 this.colWidth = this.el.getWidth();
40618 //this.gutter = this.el.getWidth();
40620 this.measureColumns();
40626 this.colYs.push( 0 );
40632 measureColumns : function()
40634 this.getContainerWidth();
40635 // if columnWidth is 0, default to outerWidth of first item
40636 if ( !this.columnWidth ) {
40637 var firstItem = this.bricks.first();
40638 Roo.log(firstItem);
40639 this.columnWidth = this.containerWidth;
40640 if (firstItem && firstItem.attr('originalwidth') ) {
40641 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40643 // columnWidth fall back to item of first element
40644 Roo.log("set column width?");
40645 this.initialColumnWidth = this.columnWidth ;
40647 // if first elem has no width, default to size of container
40652 if (this.initialColumnWidth) {
40653 this.columnWidth = this.initialColumnWidth;
40658 // column width is fixed at the top - however if container width get's smaller we should
40661 // this bit calcs how man columns..
40663 var columnWidth = this.columnWidth += this.gutter;
40665 // calculate columns
40666 var containerWidth = this.containerWidth + this.gutter;
40668 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40669 // fix rounding errors, typically with gutters
40670 var excess = columnWidth - containerWidth % columnWidth;
40673 // if overshoot is less than a pixel, round up, otherwise floor it
40674 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40675 cols = Math[ mathMethod ]( cols );
40676 this.cols = Math.max( cols, 1 );
40677 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40679 // padding positioning..
40680 var totalColWidth = this.cols * this.columnWidth;
40681 var padavail = this.containerWidth - totalColWidth;
40682 // so for 2 columns - we need 3 'pads'
40684 var padNeeded = (1+this.cols) * this.padWidth;
40686 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40688 this.columnWidth += padExtra
40689 //this.padWidth = Math.floor(padavail / ( this.cols));
40691 // adjust colum width so that padding is fixed??
40693 // we have 3 columns ... total = width * 3
40694 // we have X left over... that should be used by
40696 //if (this.expandC) {
40704 getContainerWidth : function()
40706 /* // container is parent if fit width
40707 var container = this.isFitWidth ? this.element.parentNode : this.element;
40708 // check that this.size and size are there
40709 // IE8 triggers resize on body size change, so they might not be
40711 var size = getSize( container ); //FIXME
40712 this.containerWidth = size && size.innerWidth; //FIXME
40715 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
40719 _getItemLayoutPosition : function( item ) // what is item?
40721 // we resize the item to our columnWidth..
40723 item.setWidth(this.columnWidth);
40724 item.autoBoxAdjust = false;
40726 var sz = item.getSize();
40728 // how many columns does this brick span
40729 var remainder = this.containerWidth % this.columnWidth;
40731 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40732 // round if off by 1 pixel, otherwise use ceil
40733 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
40734 colSpan = Math.min( colSpan, this.cols );
40736 // normally this should be '1' as we dont' currently allow multi width columns..
40738 var colGroup = this._getColGroup( colSpan );
40739 // get the minimum Y value from the columns
40740 var minimumY = Math.min.apply( Math, colGroup );
40741 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40743 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
40745 // position the brick
40747 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40748 y: this.currentSize.y + minimumY + this.padHeight
40752 // apply setHeight to necessary columns
40753 var setHeight = minimumY + sz.height + this.padHeight;
40754 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40756 var setSpan = this.cols + 1 - colGroup.length;
40757 for ( var i = 0; i < setSpan; i++ ) {
40758 this.colYs[ shortColIndex + i ] = setHeight ;
40765 * @param {Number} colSpan - number of columns the element spans
40766 * @returns {Array} colGroup
40768 _getColGroup : function( colSpan )
40770 if ( colSpan < 2 ) {
40771 // if brick spans only one column, use all the column Ys
40776 // how many different places could this brick fit horizontally
40777 var groupCount = this.cols + 1 - colSpan;
40778 // for each group potential horizontal position
40779 for ( var i = 0; i < groupCount; i++ ) {
40780 // make an array of colY values for that one group
40781 var groupColYs = this.colYs.slice( i, i + colSpan );
40782 // and get the max value of the array
40783 colGroup[i] = Math.max.apply( Math, groupColYs );
40788 _manageStamp : function( stamp )
40790 var stampSize = stamp.getSize();
40791 var offset = stamp.getBox();
40792 // get the columns that this stamp affects
40793 var firstX = this.isOriginLeft ? offset.x : offset.right;
40794 var lastX = firstX + stampSize.width;
40795 var firstCol = Math.floor( firstX / this.columnWidth );
40796 firstCol = Math.max( 0, firstCol );
40798 var lastCol = Math.floor( lastX / this.columnWidth );
40799 // lastCol should not go over if multiple of columnWidth #425
40800 lastCol -= lastX % this.columnWidth ? 0 : 1;
40801 lastCol = Math.min( this.cols - 1, lastCol );
40803 // set colYs to bottom of the stamp
40804 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40807 for ( var i = firstCol; i <= lastCol; i++ ) {
40808 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40813 _getContainerSize : function()
40815 this.maxY = Math.max.apply( Math, this.colYs );
40820 if ( this.isFitWidth ) {
40821 size.width = this._getContainerFitWidth();
40827 _getContainerFitWidth : function()
40829 var unusedCols = 0;
40830 // count unused columns
40833 if ( this.colYs[i] !== 0 ) {
40838 // fit container to columns that have been used
40839 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40842 needsResizeLayout : function()
40844 var previousWidth = this.containerWidth;
40845 this.getContainerWidth();
40846 return previousWidth !== this.containerWidth;
40861 * @class Roo.bootstrap.MasonryBrick
40862 * @extends Roo.bootstrap.Component
40863 * Bootstrap MasonryBrick class
40866 * Create a new MasonryBrick
40867 * @param {Object} config The config object
40870 Roo.bootstrap.MasonryBrick = function(config){
40872 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40874 Roo.bootstrap.MasonryBrick.register(this);
40880 * When a MasonryBrick is clcik
40881 * @param {Roo.bootstrap.MasonryBrick} this
40882 * @param {Roo.EventObject} e
40888 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40891 * @cfg {String} title
40895 * @cfg {String} html
40899 * @cfg {String} bgimage
40903 * @cfg {String} videourl
40907 * @cfg {String} cls
40911 * @cfg {String} href
40915 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40920 * @cfg {String} placetitle (center|bottom)
40925 * @cfg {Boolean} isFitContainer defalut true
40927 isFitContainer : true,
40930 * @cfg {Boolean} preventDefault defalut false
40932 preventDefault : false,
40935 * @cfg {Boolean} inverse defalut false
40937 maskInverse : false,
40939 getAutoCreate : function()
40941 if(!this.isFitContainer){
40942 return this.getSplitAutoCreate();
40945 var cls = 'masonry-brick masonry-brick-full';
40947 if(this.href.length){
40948 cls += ' masonry-brick-link';
40951 if(this.bgimage.length){
40952 cls += ' masonry-brick-image';
40955 if(this.maskInverse){
40956 cls += ' mask-inverse';
40959 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40960 cls += ' enable-mask';
40964 cls += ' masonry-' + this.size + '-brick';
40967 if(this.placetitle.length){
40969 switch (this.placetitle) {
40971 cls += ' masonry-center-title';
40974 cls += ' masonry-bottom-title';
40981 if(!this.html.length && !this.bgimage.length){
40982 cls += ' masonry-center-title';
40985 if(!this.html.length && this.bgimage.length){
40986 cls += ' masonry-bottom-title';
40991 cls += ' ' + this.cls;
40995 tag: (this.href.length) ? 'a' : 'div',
41000 cls: 'masonry-brick-mask'
41004 cls: 'masonry-brick-paragraph',
41010 if(this.href.length){
41011 cfg.href = this.href;
41014 var cn = cfg.cn[1].cn;
41016 if(this.title.length){
41019 cls: 'masonry-brick-title',
41024 if(this.html.length){
41027 cls: 'masonry-brick-text',
41032 if (!this.title.length && !this.html.length) {
41033 cfg.cn[1].cls += ' hide';
41036 if(this.bgimage.length){
41039 cls: 'masonry-brick-image-view',
41044 if(this.videourl.length){
41045 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41046 // youtube support only?
41049 cls: 'masonry-brick-image-view',
41052 allowfullscreen : true
41060 getSplitAutoCreate : function()
41062 var cls = 'masonry-brick masonry-brick-split';
41064 if(this.href.length){
41065 cls += ' masonry-brick-link';
41068 if(this.bgimage.length){
41069 cls += ' masonry-brick-image';
41073 cls += ' masonry-' + this.size + '-brick';
41076 switch (this.placetitle) {
41078 cls += ' masonry-center-title';
41081 cls += ' masonry-bottom-title';
41084 if(!this.bgimage.length){
41085 cls += ' masonry-center-title';
41088 if(this.bgimage.length){
41089 cls += ' masonry-bottom-title';
41095 cls += ' ' + this.cls;
41099 tag: (this.href.length) ? 'a' : 'div',
41104 cls: 'masonry-brick-split-head',
41108 cls: 'masonry-brick-paragraph',
41115 cls: 'masonry-brick-split-body',
41121 if(this.href.length){
41122 cfg.href = this.href;
41125 if(this.title.length){
41126 cfg.cn[0].cn[0].cn.push({
41128 cls: 'masonry-brick-title',
41133 if(this.html.length){
41134 cfg.cn[1].cn.push({
41136 cls: 'masonry-brick-text',
41141 if(this.bgimage.length){
41142 cfg.cn[0].cn.push({
41144 cls: 'masonry-brick-image-view',
41149 if(this.videourl.length){
41150 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41151 // youtube support only?
41152 cfg.cn[0].cn.cn.push({
41154 cls: 'masonry-brick-image-view',
41157 allowfullscreen : true
41164 initEvents: function()
41166 switch (this.size) {
41199 this.el.on('touchstart', this.onTouchStart, this);
41200 this.el.on('touchmove', this.onTouchMove, this);
41201 this.el.on('touchend', this.onTouchEnd, this);
41202 this.el.on('contextmenu', this.onContextMenu, this);
41204 this.el.on('mouseenter' ,this.enter, this);
41205 this.el.on('mouseleave', this.leave, this);
41206 this.el.on('click', this.onClick, this);
41209 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41210 this.parent().bricks.push(this);
41215 onClick: function(e, el)
41217 var time = this.endTimer - this.startTimer;
41218 // Roo.log(e.preventDefault());
41221 e.preventDefault();
41226 if(!this.preventDefault){
41230 e.preventDefault();
41232 if (this.activeClass != '') {
41233 this.selectBrick();
41236 this.fireEvent('click', this, e);
41239 enter: function(e, el)
41241 e.preventDefault();
41243 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41247 if(this.bgimage.length && this.html.length){
41248 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41252 leave: function(e, el)
41254 e.preventDefault();
41256 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41260 if(this.bgimage.length && this.html.length){
41261 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41265 onTouchStart: function(e, el)
41267 // e.preventDefault();
41269 this.touchmoved = false;
41271 if(!this.isFitContainer){
41275 if(!this.bgimage.length || !this.html.length){
41279 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41281 this.timer = new Date().getTime();
41285 onTouchMove: function(e, el)
41287 this.touchmoved = true;
41290 onContextMenu : function(e,el)
41292 e.preventDefault();
41293 e.stopPropagation();
41297 onTouchEnd: function(e, el)
41299 // e.preventDefault();
41301 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41308 if(!this.bgimage.length || !this.html.length){
41310 if(this.href.length){
41311 window.location.href = this.href;
41317 if(!this.isFitContainer){
41321 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41323 window.location.href = this.href;
41326 //selection on single brick only
41327 selectBrick : function() {
41329 if (!this.parentId) {
41333 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41334 var index = m.selectedBrick.indexOf(this.id);
41337 m.selectedBrick.splice(index,1);
41338 this.el.removeClass(this.activeClass);
41342 for(var i = 0; i < m.selectedBrick.length; i++) {
41343 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41344 b.el.removeClass(b.activeClass);
41347 m.selectedBrick = [];
41349 m.selectedBrick.push(this.id);
41350 this.el.addClass(this.activeClass);
41354 isSelected : function(){
41355 return this.el.hasClass(this.activeClass);
41360 Roo.apply(Roo.bootstrap.MasonryBrick, {
41363 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41365 * register a Masonry Brick
41366 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41369 register : function(brick)
41371 //this.groups[brick.id] = brick;
41372 this.groups.add(brick.id, brick);
41375 * fetch a masonry brick based on the masonry brick ID
41376 * @param {string} the masonry brick to add
41377 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41380 get: function(brick_id)
41382 // if (typeof(this.groups[brick_id]) == 'undefined') {
41385 // return this.groups[brick_id] ;
41387 if(this.groups.key(brick_id)) {
41388 return this.groups.key(brick_id);
41406 * @class Roo.bootstrap.Brick
41407 * @extends Roo.bootstrap.Component
41408 * Bootstrap Brick class
41411 * Create a new Brick
41412 * @param {Object} config The config object
41415 Roo.bootstrap.Brick = function(config){
41416 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41422 * When a Brick is click
41423 * @param {Roo.bootstrap.Brick} this
41424 * @param {Roo.EventObject} e
41430 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
41433 * @cfg {String} title
41437 * @cfg {String} html
41441 * @cfg {String} bgimage
41445 * @cfg {String} cls
41449 * @cfg {String} href
41453 * @cfg {String} video
41457 * @cfg {Boolean} square
41461 getAutoCreate : function()
41463 var cls = 'roo-brick';
41465 if(this.href.length){
41466 cls += ' roo-brick-link';
41469 if(this.bgimage.length){
41470 cls += ' roo-brick-image';
41473 if(!this.html.length && !this.bgimage.length){
41474 cls += ' roo-brick-center-title';
41477 if(!this.html.length && this.bgimage.length){
41478 cls += ' roo-brick-bottom-title';
41482 cls += ' ' + this.cls;
41486 tag: (this.href.length) ? 'a' : 'div',
41491 cls: 'roo-brick-paragraph',
41497 if(this.href.length){
41498 cfg.href = this.href;
41501 var cn = cfg.cn[0].cn;
41503 if(this.title.length){
41506 cls: 'roo-brick-title',
41511 if(this.html.length){
41514 cls: 'roo-brick-text',
41521 if(this.bgimage.length){
41524 cls: 'roo-brick-image-view',
41532 initEvents: function()
41534 if(this.title.length || this.html.length){
41535 this.el.on('mouseenter' ,this.enter, this);
41536 this.el.on('mouseleave', this.leave, this);
41539 Roo.EventManager.onWindowResize(this.resize, this);
41541 if(this.bgimage.length){
41542 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41543 this.imageEl.on('load', this.onImageLoad, this);
41550 onImageLoad : function()
41555 resize : function()
41557 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41559 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41561 if(this.bgimage.length){
41562 var image = this.el.select('.roo-brick-image-view', true).first();
41564 image.setWidth(paragraph.getWidth());
41567 image.setHeight(paragraph.getWidth());
41570 this.el.setHeight(image.getHeight());
41571 paragraph.setHeight(image.getHeight());
41577 enter: function(e, el)
41579 e.preventDefault();
41581 if(this.bgimage.length){
41582 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41583 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41587 leave: function(e, el)
41589 e.preventDefault();
41591 if(this.bgimage.length){
41592 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41593 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41608 * @class Roo.bootstrap.form.NumberField
41609 * @extends Roo.bootstrap.form.Input
41610 * Bootstrap NumberField class
41616 * Create a new NumberField
41617 * @param {Object} config The config object
41620 Roo.bootstrap.form.NumberField = function(config){
41621 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41624 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41627 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41629 allowDecimals : true,
41631 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41633 decimalSeparator : ".",
41635 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41637 decimalPrecision : 2,
41639 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41641 allowNegative : true,
41644 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41648 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41650 minValue : Number.NEGATIVE_INFINITY,
41652 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41654 maxValue : Number.MAX_VALUE,
41656 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41658 minText : "The minimum value for this field is {0}",
41660 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41662 maxText : "The maximum value for this field is {0}",
41664 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41665 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41667 nanText : "{0} is not a valid number",
41669 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41671 thousandsDelimiter : false,
41673 * @cfg {String} valueAlign alignment of value
41675 valueAlign : "left",
41677 getAutoCreate : function()
41679 var hiddenInput = {
41683 cls: 'hidden-number-input'
41687 hiddenInput.name = this.name;
41692 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41694 this.name = hiddenInput.name;
41696 if(cfg.cn.length > 0) {
41697 cfg.cn.push(hiddenInput);
41704 initEvents : function()
41706 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41708 var allowed = "0123456789";
41710 if(this.allowDecimals){
41711 allowed += this.decimalSeparator;
41714 if(this.allowNegative){
41718 if(this.thousandsDelimiter) {
41722 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41724 var keyPress = function(e){
41726 var k = e.getKey();
41728 var c = e.getCharCode();
41731 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41732 allowed.indexOf(String.fromCharCode(c)) === -1
41738 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41742 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41747 this.el.on("keypress", keyPress, this);
41750 validateValue : function(value)
41753 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41757 var num = this.parseValue(value);
41760 this.markInvalid(String.format(this.nanText, value));
41764 if(num < this.minValue){
41765 this.markInvalid(String.format(this.minText, this.minValue));
41769 if(num > this.maxValue){
41770 this.markInvalid(String.format(this.maxText, this.maxValue));
41777 getValue : function()
41779 var v = this.hiddenEl().getValue();
41781 return this.fixPrecision(this.parseValue(v));
41784 parseValue : function(value)
41786 if(this.thousandsDelimiter) {
41788 r = new RegExp(",", "g");
41789 value = value.replace(r, "");
41792 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41793 return isNaN(value) ? '' : value;
41796 fixPrecision : function(value)
41798 if(this.thousandsDelimiter) {
41800 r = new RegExp(",", "g");
41801 value = value.replace(r, "");
41804 var nan = isNaN(value);
41806 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41807 return nan ? '' : value;
41809 return parseFloat(value).toFixed(this.decimalPrecision);
41812 setValue : function(v)
41814 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41820 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41822 this.inputEl().dom.value = (v == '') ? '' :
41823 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41825 if(!this.allowZero && v === '0') {
41826 this.hiddenEl().dom.value = '';
41827 this.inputEl().dom.value = '';
41834 decimalPrecisionFcn : function(v)
41836 return Math.floor(v);
41839 beforeBlur : function()
41841 var v = this.parseValue(this.getRawValue());
41843 if(v || v === 0 || v === ''){
41848 hiddenEl : function()
41850 return this.el.select('input.hidden-number-input',true).first();
41862 * @class Roo.bootstrap.DocumentSlider
41863 * @extends Roo.bootstrap.Component
41864 * Bootstrap DocumentSlider class
41867 * Create a new DocumentViewer
41868 * @param {Object} config The config object
41871 Roo.bootstrap.DocumentSlider = function(config){
41872 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41879 * Fire after initEvent
41880 * @param {Roo.bootstrap.DocumentSlider} this
41885 * Fire after update
41886 * @param {Roo.bootstrap.DocumentSlider} this
41892 * @param {Roo.bootstrap.DocumentSlider} this
41898 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41904 getAutoCreate : function()
41908 cls : 'roo-document-slider',
41912 cls : 'roo-document-slider-header',
41916 cls : 'roo-document-slider-header-title'
41922 cls : 'roo-document-slider-body',
41926 cls : 'roo-document-slider-prev',
41930 cls : 'fa fa-chevron-left'
41936 cls : 'roo-document-slider-thumb',
41940 cls : 'roo-document-slider-image'
41946 cls : 'roo-document-slider-next',
41950 cls : 'fa fa-chevron-right'
41962 initEvents : function()
41964 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41965 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41967 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41968 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41970 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41971 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41973 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41974 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41976 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41977 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41979 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41980 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41982 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41983 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41985 this.thumbEl.on('click', this.onClick, this);
41987 this.prevIndicator.on('click', this.prev, this);
41989 this.nextIndicator.on('click', this.next, this);
41993 initial : function()
41995 if(this.files.length){
41996 this.indicator = 1;
42000 this.fireEvent('initial', this);
42003 update : function()
42005 this.imageEl.attr('src', this.files[this.indicator - 1]);
42007 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42009 this.prevIndicator.show();
42011 if(this.indicator == 1){
42012 this.prevIndicator.hide();
42015 this.nextIndicator.show();
42017 if(this.indicator == this.files.length){
42018 this.nextIndicator.hide();
42021 this.thumbEl.scrollTo('top');
42023 this.fireEvent('update', this);
42026 onClick : function(e)
42028 e.preventDefault();
42030 this.fireEvent('click', this);
42035 e.preventDefault();
42037 this.indicator = Math.max(1, this.indicator - 1);
42044 e.preventDefault();
42046 this.indicator = Math.min(this.files.length, this.indicator + 1);
42060 * @class Roo.bootstrap.form.RadioSet
42061 * @extends Roo.bootstrap.form.Input
42062 * @children Roo.bootstrap.form.Radio
42063 * Bootstrap RadioSet class
42064 * @cfg {String} indicatorpos (left|right) default left
42065 * @cfg {Boolean} inline (true|false) inline the element (default true)
42066 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42068 * Create a new RadioSet
42069 * @param {Object} config The config object
42072 Roo.bootstrap.form.RadioSet = function(config){
42074 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42078 Roo.bootstrap.form.RadioSet.register(this);
42083 * Fires when the element is checked or unchecked.
42084 * @param {Roo.bootstrap.form.RadioSet} this This radio
42085 * @param {Roo.bootstrap.form.Radio} item The checked item
42090 * Fires when the element is click.
42091 * @param {Roo.bootstrap.form.RadioSet} this This radio set
42092 * @param {Roo.bootstrap.form.Radio} item The checked item
42093 * @param {Roo.EventObject} e The event object
42100 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
42108 indicatorpos : 'left',
42110 getAutoCreate : function()
42114 cls : 'roo-radio-set-label',
42118 html : this.fieldLabel
42122 if (Roo.bootstrap.version == 3) {
42125 if(this.indicatorpos == 'left'){
42128 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42129 tooltip : 'This field is required'
42134 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42135 tooltip : 'This field is required'
42141 cls : 'roo-radio-set-items'
42144 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42146 if (align === 'left' && this.fieldLabel.length) {
42149 cls : "roo-radio-set-right",
42155 if(this.labelWidth > 12){
42156 label.style = "width: " + this.labelWidth + 'px';
42159 if(this.labelWidth < 13 && this.labelmd == 0){
42160 this.labelmd = this.labelWidth;
42163 if(this.labellg > 0){
42164 label.cls += ' col-lg-' + this.labellg;
42165 items.cls += ' col-lg-' + (12 - this.labellg);
42168 if(this.labelmd > 0){
42169 label.cls += ' col-md-' + this.labelmd;
42170 items.cls += ' col-md-' + (12 - this.labelmd);
42173 if(this.labelsm > 0){
42174 label.cls += ' col-sm-' + this.labelsm;
42175 items.cls += ' col-sm-' + (12 - this.labelsm);
42178 if(this.labelxs > 0){
42179 label.cls += ' col-xs-' + this.labelxs;
42180 items.cls += ' col-xs-' + (12 - this.labelxs);
42186 cls : 'roo-radio-set',
42190 cls : 'roo-radio-set-input',
42193 value : this.value ? this.value : ''
42200 if(this.weight.length){
42201 cfg.cls += ' roo-radio-' + this.weight;
42205 cfg.cls += ' roo-radio-set-inline';
42209 ['xs','sm','md','lg'].map(function(size){
42210 if (settings[size]) {
42211 cfg.cls += ' col-' + size + '-' + settings[size];
42219 initEvents : function()
42221 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42222 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42224 if(!this.fieldLabel.length){
42225 this.labelEl.hide();
42228 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42229 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42231 this.indicator = this.indicatorEl();
42233 if(this.indicator){
42234 this.indicator.addClass('invisible');
42237 this.originalValue = this.getValue();
42241 inputEl: function ()
42243 return this.el.select('.roo-radio-set-input', true).first();
42246 getChildContainer : function()
42248 return this.itemsEl;
42251 register : function(item)
42253 this.radioes.push(item);
42257 validate : function()
42259 if(this.getVisibilityEl().hasClass('hidden')){
42265 Roo.each(this.radioes, function(i){
42274 if(this.allowBlank) {
42278 if(this.disabled || valid){
42283 this.markInvalid();
42288 markValid : function()
42290 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42291 this.indicatorEl().removeClass('visible');
42292 this.indicatorEl().addClass('invisible');
42296 if (Roo.bootstrap.version == 3) {
42297 this.el.removeClass([this.invalidClass, this.validClass]);
42298 this.el.addClass(this.validClass);
42300 this.el.removeClass(['is-invalid','is-valid']);
42301 this.el.addClass(['is-valid']);
42303 this.fireEvent('valid', this);
42306 markInvalid : function(msg)
42308 if(this.allowBlank || this.disabled){
42312 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42313 this.indicatorEl().removeClass('invisible');
42314 this.indicatorEl().addClass('visible');
42316 if (Roo.bootstrap.version == 3) {
42317 this.el.removeClass([this.invalidClass, this.validClass]);
42318 this.el.addClass(this.invalidClass);
42320 this.el.removeClass(['is-invalid','is-valid']);
42321 this.el.addClass(['is-invalid']);
42324 this.fireEvent('invalid', this, msg);
42328 setValue : function(v, suppressEvent)
42330 if(this.value === v){
42337 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42340 Roo.each(this.radioes, function(i){
42342 i.el.removeClass('checked');
42345 Roo.each(this.radioes, function(i){
42347 if(i.value === v || i.value.toString() === v.toString()){
42349 i.el.addClass('checked');
42351 if(suppressEvent !== true){
42352 this.fireEvent('check', this, i);
42363 clearInvalid : function(){
42365 if(!this.el || this.preventMark){
42369 this.el.removeClass([this.invalidClass]);
42371 this.fireEvent('valid', this);
42376 Roo.apply(Roo.bootstrap.form.RadioSet, {
42380 register : function(set)
42382 this.groups[set.name] = set;
42385 get: function(name)
42387 if (typeof(this.groups[name]) == 'undefined') {
42391 return this.groups[name] ;
42397 * Ext JS Library 1.1.1
42398 * Copyright(c) 2006-2007, Ext JS, LLC.
42400 * Originally Released Under LGPL - original licence link has changed is not relivant.
42403 * <script type="text/javascript">
42408 * @class Roo.bootstrap.SplitBar
42409 * @extends Roo.util.Observable
42410 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42414 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42415 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42416 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42417 split.minSize = 100;
42418 split.maxSize = 600;
42419 split.animate = true;
42420 split.on('moved', splitterMoved);
42423 * Create a new SplitBar
42424 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
42425 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
42426 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42427 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
42428 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42429 position of the SplitBar).
42431 Roo.bootstrap.SplitBar = function(cfg){
42436 // dragElement : elm
42437 // resizingElement: el,
42439 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42440 // placement : Roo.bootstrap.SplitBar.LEFT ,
42441 // existingProxy ???
42444 this.el = Roo.get(cfg.dragElement, true);
42445 this.el.dom.unselectable = "on";
42447 this.resizingEl = Roo.get(cfg.resizingElement, true);
42451 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42452 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42455 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42458 * The minimum size of the resizing element. (Defaults to 0)
42464 * The maximum size of the resizing element. (Defaults to 2000)
42467 this.maxSize = 2000;
42470 * Whether to animate the transition to the new size
42473 this.animate = false;
42476 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42479 this.useShim = false;
42484 if(!cfg.existingProxy){
42486 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42488 this.proxy = Roo.get(cfg.existingProxy).dom;
42491 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42494 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42497 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42500 this.dragSpecs = {};
42503 * @private The adapter to use to positon and resize elements
42505 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42506 this.adapter.init(this);
42508 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42510 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42511 this.el.addClass("roo-splitbar-h");
42514 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42515 this.el.addClass("roo-splitbar-v");
42521 * Fires when the splitter is moved (alias for {@link #event-moved})
42522 * @param {Roo.bootstrap.SplitBar} this
42523 * @param {Number} newSize the new width or height
42528 * Fires when the splitter is moved
42529 * @param {Roo.bootstrap.SplitBar} this
42530 * @param {Number} newSize the new width or height
42534 * @event beforeresize
42535 * Fires before the splitter is dragged
42536 * @param {Roo.bootstrap.SplitBar} this
42538 "beforeresize" : true,
42540 "beforeapply" : true
42543 Roo.util.Observable.call(this);
42546 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42547 onStartProxyDrag : function(x, y){
42548 this.fireEvent("beforeresize", this);
42550 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
42552 o.enableDisplayMode("block");
42553 // all splitbars share the same overlay
42554 Roo.bootstrap.SplitBar.prototype.overlay = o;
42556 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42557 this.overlay.show();
42558 Roo.get(this.proxy).setDisplayed("block");
42559 var size = this.adapter.getElementSize(this);
42560 this.activeMinSize = this.getMinimumSize();;
42561 this.activeMaxSize = this.getMaximumSize();;
42562 var c1 = size - this.activeMinSize;
42563 var c2 = Math.max(this.activeMaxSize - size, 0);
42564 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42565 this.dd.resetConstraints();
42566 this.dd.setXConstraint(
42567 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
42568 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42570 this.dd.setYConstraint(0, 0);
42572 this.dd.resetConstraints();
42573 this.dd.setXConstraint(0, 0);
42574 this.dd.setYConstraint(
42575 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
42576 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42579 this.dragSpecs.startSize = size;
42580 this.dragSpecs.startPoint = [x, y];
42581 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42585 * @private Called after the drag operation by the DDProxy
42587 onEndProxyDrag : function(e){
42588 Roo.get(this.proxy).setDisplayed(false);
42589 var endPoint = Roo.lib.Event.getXY(e);
42591 this.overlay.hide();
42594 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42595 newSize = this.dragSpecs.startSize +
42596 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42597 endPoint[0] - this.dragSpecs.startPoint[0] :
42598 this.dragSpecs.startPoint[0] - endPoint[0]
42601 newSize = this.dragSpecs.startSize +
42602 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42603 endPoint[1] - this.dragSpecs.startPoint[1] :
42604 this.dragSpecs.startPoint[1] - endPoint[1]
42607 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42608 if(newSize != this.dragSpecs.startSize){
42609 if(this.fireEvent('beforeapply', this, newSize) !== false){
42610 this.adapter.setElementSize(this, newSize);
42611 this.fireEvent("moved", this, newSize);
42612 this.fireEvent("resize", this, newSize);
42618 * Get the adapter this SplitBar uses
42619 * @return The adapter object
42621 getAdapter : function(){
42622 return this.adapter;
42626 * Set the adapter this SplitBar uses
42627 * @param {Object} adapter A SplitBar adapter object
42629 setAdapter : function(adapter){
42630 this.adapter = adapter;
42631 this.adapter.init(this);
42635 * Gets the minimum size for the resizing element
42636 * @return {Number} The minimum size
42638 getMinimumSize : function(){
42639 return this.minSize;
42643 * Sets the minimum size for the resizing element
42644 * @param {Number} minSize The minimum size
42646 setMinimumSize : function(minSize){
42647 this.minSize = minSize;
42651 * Gets the maximum size for the resizing element
42652 * @return {Number} The maximum size
42654 getMaximumSize : function(){
42655 return this.maxSize;
42659 * Sets the maximum size for the resizing element
42660 * @param {Number} maxSize The maximum size
42662 setMaximumSize : function(maxSize){
42663 this.maxSize = maxSize;
42667 * Sets the initialize size for the resizing element
42668 * @param {Number} size The initial size
42670 setCurrentSize : function(size){
42671 var oldAnimate = this.animate;
42672 this.animate = false;
42673 this.adapter.setElementSize(this, size);
42674 this.animate = oldAnimate;
42678 * Destroy this splitbar.
42679 * @param {Boolean} removeEl True to remove the element
42681 destroy : function(removeEl){
42683 this.shim.remove();
42686 this.proxy.parentNode.removeChild(this.proxy);
42694 * @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.
42696 Roo.bootstrap.SplitBar.createProxy = function(dir){
42697 var proxy = new Roo.Element(document.createElement("div"));
42698 proxy.unselectable();
42699 var cls = 'roo-splitbar-proxy';
42700 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42701 document.body.appendChild(proxy.dom);
42706 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42707 * Default Adapter. It assumes the splitter and resizing element are not positioned
42708 * elements and only gets/sets the width of the element. Generally used for table based layouts.
42710 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42713 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42714 // do nothing for now
42715 init : function(s){
42719 * Called before drag operations to get the current size of the resizing element.
42720 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42722 getElementSize : function(s){
42723 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42724 return s.resizingEl.getWidth();
42726 return s.resizingEl.getHeight();
42731 * Called after drag operations to set the size of the resizing element.
42732 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42733 * @param {Number} newSize The new size to set
42734 * @param {Function} onComplete A function to be invoked when resizing is complete
42736 setElementSize : function(s, newSize, onComplete){
42737 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42739 s.resizingEl.setWidth(newSize);
42741 onComplete(s, newSize);
42744 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42749 s.resizingEl.setHeight(newSize);
42751 onComplete(s, newSize);
42754 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42761 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42762 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42763 * Adapter that moves the splitter element to align with the resized sizing element.
42764 * Used with an absolute positioned SplitBar.
42765 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42766 * document.body, make sure you assign an id to the body element.
42768 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42769 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42770 this.container = Roo.get(container);
42773 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42774 init : function(s){
42775 this.basic.init(s);
42778 getElementSize : function(s){
42779 return this.basic.getElementSize(s);
42782 setElementSize : function(s, newSize, onComplete){
42783 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42786 moveSplitter : function(s){
42787 var yes = Roo.bootstrap.SplitBar;
42788 switch(s.placement){
42790 s.el.setX(s.resizingEl.getRight());
42793 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42796 s.el.setY(s.resizingEl.getBottom());
42799 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42806 * Orientation constant - Create a vertical SplitBar
42810 Roo.bootstrap.SplitBar.VERTICAL = 1;
42813 * Orientation constant - Create a horizontal SplitBar
42817 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42820 * Placement constant - The resizing element is to the left of the splitter element
42824 Roo.bootstrap.SplitBar.LEFT = 1;
42827 * Placement constant - The resizing element is to the right of the splitter element
42831 Roo.bootstrap.SplitBar.RIGHT = 2;
42834 * Placement constant - The resizing element is positioned above the splitter element
42838 Roo.bootstrap.SplitBar.TOP = 3;
42841 * Placement constant - The resizing element is positioned under splitter element
42845 Roo.bootstrap.SplitBar.BOTTOM = 4;
42848 * Ext JS Library 1.1.1
42849 * Copyright(c) 2006-2007, Ext JS, LLC.
42851 * Originally Released Under LGPL - original licence link has changed is not relivant.
42854 * <script type="text/javascript">
42858 * @class Roo.bootstrap.layout.Manager
42859 * @extends Roo.bootstrap.Component
42861 * Base class for layout managers.
42863 Roo.bootstrap.layout.Manager = function(config)
42865 this.monitorWindowResize = true; // do this before we apply configuration.
42867 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42873 /** false to disable window resize monitoring @type Boolean */
42879 * Fires when a layout is performed.
42880 * @param {Roo.LayoutManager} this
42884 * @event regionresized
42885 * Fires when the user resizes a region.
42886 * @param {Roo.LayoutRegion} region The resized region
42887 * @param {Number} newSize The new size (width for east/west, height for north/south)
42889 "regionresized" : true,
42891 * @event regioncollapsed
42892 * Fires when a region is collapsed.
42893 * @param {Roo.LayoutRegion} region The collapsed region
42895 "regioncollapsed" : true,
42897 * @event regionexpanded
42898 * Fires when a region is expanded.
42899 * @param {Roo.LayoutRegion} region The expanded region
42901 "regionexpanded" : true
42903 this.updating = false;
42906 this.el = Roo.get(config.el);
42912 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42917 monitorWindowResize : true,
42923 onRender : function(ct, position)
42926 this.el = Roo.get(ct);
42929 //this.fireEvent('render',this);
42933 initEvents: function()
42937 // ie scrollbar fix
42938 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42939 document.body.scroll = "no";
42940 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42941 this.el.position('relative');
42943 this.id = this.el.id;
42944 this.el.addClass("roo-layout-container");
42945 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42946 if(this.el.dom != document.body ) {
42947 this.el.on('resize', this.layout,this);
42948 this.el.on('show', this.layout,this);
42954 * Returns true if this layout is currently being updated
42955 * @return {Boolean}
42957 isUpdating : function(){
42958 return this.updating;
42962 * Suspend the LayoutManager from doing auto-layouts while
42963 * making multiple add or remove calls
42965 beginUpdate : function(){
42966 this.updating = true;
42970 * Restore auto-layouts and optionally disable the manager from performing a layout
42971 * @param {Boolean} noLayout true to disable a layout update
42973 endUpdate : function(noLayout){
42974 this.updating = false;
42980 layout: function(){
42984 onRegionResized : function(region, newSize){
42985 this.fireEvent("regionresized", region, newSize);
42989 onRegionCollapsed : function(region){
42990 this.fireEvent("regioncollapsed", region);
42993 onRegionExpanded : function(region){
42994 this.fireEvent("regionexpanded", region);
42998 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42999 * performs box-model adjustments.
43000 * @return {Object} The size as an object {width: (the width), height: (the height)}
43002 getViewSize : function()
43005 if(this.el.dom != document.body){
43006 size = this.el.getSize();
43008 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43010 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43011 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43016 * Returns the Element this layout is bound to.
43017 * @return {Roo.Element}
43019 getEl : function(){
43024 * Returns the specified region.
43025 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43026 * @return {Roo.LayoutRegion}
43028 getRegion : function(target){
43029 return this.regions[target.toLowerCase()];
43032 onWindowResize : function(){
43033 if(this.monitorWindowResize){
43040 * Ext JS Library 1.1.1
43041 * Copyright(c) 2006-2007, Ext JS, LLC.
43043 * Originally Released Under LGPL - original licence link has changed is not relivant.
43046 * <script type="text/javascript">
43049 * @class Roo.bootstrap.layout.Border
43050 * @extends Roo.bootstrap.layout.Manager
43051 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43052 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43053 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43054 * please see: examples/bootstrap/nested.html<br><br>
43056 <b>The container the layout is rendered into can be either the body element or any other element.
43057 If it is not the body element, the container needs to either be an absolute positioned element,
43058 or you will need to add "position:relative" to the css of the container. You will also need to specify
43059 the container size if it is not the body element.</b>
43062 * Create a new Border
43063 * @param {Object} config Configuration options
43065 Roo.bootstrap.layout.Border = function(config){
43066 config = config || {};
43067 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43071 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43072 if(config[region]){
43073 config[region].region = region;
43074 this.addRegion(config[region]);
43080 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
43082 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43085 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43088 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43091 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43094 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43097 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43103 parent : false, // this might point to a 'nest' or a ???
43106 * Creates and adds a new region if it doesn't already exist.
43107 * @param {String} target The target region key (north, south, east, west or center).
43108 * @param {Object} config The regions config object
43109 * @return {BorderLayoutRegion} The new region
43111 addRegion : function(config)
43113 if(!this.regions[config.region]){
43114 var r = this.factory(config);
43115 this.bindRegion(r);
43117 return this.regions[config.region];
43121 bindRegion : function(r){
43122 this.regions[r.config.region] = r;
43124 r.on("visibilitychange", this.layout, this);
43125 r.on("paneladded", this.layout, this);
43126 r.on("panelremoved", this.layout, this);
43127 r.on("invalidated", this.layout, this);
43128 r.on("resized", this.onRegionResized, this);
43129 r.on("collapsed", this.onRegionCollapsed, this);
43130 r.on("expanded", this.onRegionExpanded, this);
43134 * Performs a layout update.
43136 layout : function()
43138 if(this.updating) {
43142 // render all the rebions if they have not been done alreayd?
43143 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43144 if(this.regions[region] && !this.regions[region].bodyEl){
43145 this.regions[region].onRender(this.el)
43149 var size = this.getViewSize();
43150 var w = size.width;
43151 var h = size.height;
43156 //var x = 0, y = 0;
43158 var rs = this.regions;
43159 var north = rs["north"];
43160 var south = rs["south"];
43161 var west = rs["west"];
43162 var east = rs["east"];
43163 var center = rs["center"];
43164 //if(this.hideOnLayout){ // not supported anymore
43165 //c.el.setStyle("display", "none");
43167 if(north && north.isVisible()){
43168 var b = north.getBox();
43169 var m = north.getMargins();
43170 b.width = w - (m.left+m.right);
43173 centerY = b.height + b.y + m.bottom;
43174 centerH -= centerY;
43175 north.updateBox(this.safeBox(b));
43177 if(south && south.isVisible()){
43178 var b = south.getBox();
43179 var m = south.getMargins();
43180 b.width = w - (m.left+m.right);
43182 var totalHeight = (b.height + m.top + m.bottom);
43183 b.y = h - totalHeight + m.top;
43184 centerH -= totalHeight;
43185 south.updateBox(this.safeBox(b));
43187 if(west && west.isVisible()){
43188 var b = west.getBox();
43189 var m = west.getMargins();
43190 b.height = centerH - (m.top+m.bottom);
43192 b.y = centerY + m.top;
43193 var totalWidth = (b.width + m.left + m.right);
43194 centerX += totalWidth;
43195 centerW -= totalWidth;
43196 west.updateBox(this.safeBox(b));
43198 if(east && east.isVisible()){
43199 var b = east.getBox();
43200 var m = east.getMargins();
43201 b.height = centerH - (m.top+m.bottom);
43202 var totalWidth = (b.width + m.left + m.right);
43203 b.x = w - totalWidth + m.left;
43204 b.y = centerY + m.top;
43205 centerW -= totalWidth;
43206 east.updateBox(this.safeBox(b));
43209 var m = center.getMargins();
43211 x: centerX + m.left,
43212 y: centerY + m.top,
43213 width: centerW - (m.left+m.right),
43214 height: centerH - (m.top+m.bottom)
43216 //if(this.hideOnLayout){
43217 //center.el.setStyle("display", "block");
43219 center.updateBox(this.safeBox(centerBox));
43222 this.fireEvent("layout", this);
43226 safeBox : function(box){
43227 box.width = Math.max(0, box.width);
43228 box.height = Math.max(0, box.height);
43233 * Adds a ContentPanel (or subclass) to this layout.
43234 * @param {String} target The target region key (north, south, east, west or center).
43235 * @param {Roo.ContentPanel} panel The panel to add
43236 * @return {Roo.ContentPanel} The added panel
43238 add : function(target, panel){
43240 target = target.toLowerCase();
43241 return this.regions[target].add(panel);
43245 * Remove a ContentPanel (or subclass) to this layout.
43246 * @param {String} target The target region key (north, south, east, west or center).
43247 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43248 * @return {Roo.ContentPanel} The removed panel
43250 remove : function(target, panel){
43251 target = target.toLowerCase();
43252 return this.regions[target].remove(panel);
43256 * Searches all regions for a panel with the specified id
43257 * @param {String} panelId
43258 * @return {Roo.ContentPanel} The panel or null if it wasn't found
43260 findPanel : function(panelId){
43261 var rs = this.regions;
43262 for(var target in rs){
43263 if(typeof rs[target] != "function"){
43264 var p = rs[target].getPanel(panelId);
43274 * Searches all regions for a panel with the specified id and activates (shows) it.
43275 * @param {String/ContentPanel} panelId The panels id or the panel itself
43276 * @return {Roo.ContentPanel} The shown panel or null
43278 showPanel : function(panelId) {
43279 var rs = this.regions;
43280 for(var target in rs){
43281 var r = rs[target];
43282 if(typeof r != "function"){
43283 if(r.hasPanel(panelId)){
43284 return r.showPanel(panelId);
43292 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43293 * @param {Roo.state.Provider} provider (optional) An alternate state provider
43296 restoreState : function(provider){
43298 provider = Roo.state.Manager;
43300 var sm = new Roo.LayoutStateManager();
43301 sm.init(this, provider);
43307 * Adds a xtype elements to the layout.
43311 xtype : 'ContentPanel',
43318 xtype : 'NestedLayoutPanel',
43324 items : [ ... list of content panels or nested layout panels.. ]
43328 * @param {Object} cfg Xtype definition of item to add.
43330 addxtype : function(cfg)
43332 // basically accepts a pannel...
43333 // can accept a layout region..!?!?
43334 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43337 // theory? children can only be panels??
43339 //if (!cfg.xtype.match(/Panel$/)) {
43344 if (typeof(cfg.region) == 'undefined') {
43345 Roo.log("Failed to add Panel, region was not set");
43349 var region = cfg.region;
43355 xitems = cfg.items;
43360 if ( region == 'center') {
43361 Roo.log("Center: " + cfg.title);
43367 case 'Content': // ContentPanel (el, cfg)
43368 case 'Scroll': // ContentPanel (el, cfg)
43370 cfg.autoCreate = cfg.autoCreate || true;
43371 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43373 // var el = this.el.createChild();
43374 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43377 this.add(region, ret);
43381 case 'TreePanel': // our new panel!
43382 cfg.el = this.el.createChild();
43383 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43384 this.add(region, ret);
43389 // create a new Layout (which is a Border Layout...
43391 var clayout = cfg.layout;
43392 clayout.el = this.el.createChild();
43393 clayout.items = clayout.items || [];
43397 // replace this exitems with the clayout ones..
43398 xitems = clayout.items;
43400 // force background off if it's in center...
43401 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43402 cfg.background = false;
43404 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
43407 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43408 //console.log('adding nested layout panel ' + cfg.toSource());
43409 this.add(region, ret);
43410 nb = {}; /// find first...
43415 // needs grid and region
43417 //var el = this.getRegion(region).el.createChild();
43419 *var el = this.el.createChild();
43420 // create the grid first...
43421 cfg.grid.container = el;
43422 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43425 if (region == 'center' && this.active ) {
43426 cfg.background = false;
43429 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43431 this.add(region, ret);
43433 if (cfg.background) {
43434 // render grid on panel activation (if panel background)
43435 ret.on('activate', function(gp) {
43436 if (!gp.grid.rendered) {
43437 // gp.grid.render(el);
43441 // cfg.grid.render(el);
43447 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43448 // it was the old xcomponent building that caused this before.
43449 // espeically if border is the top element in the tree.
43459 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43461 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43462 this.add(region, ret);
43466 throw "Can not add '" + cfg.xtype + "' to Border";
43472 this.beginUpdate();
43476 Roo.each(xitems, function(i) {
43477 region = nb && i.region ? i.region : false;
43479 var add = ret.addxtype(i);
43482 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43483 if (!i.background) {
43484 abn[region] = nb[region] ;
43491 // make the last non-background panel active..
43492 //if (nb) { Roo.log(abn); }
43495 for(var r in abn) {
43496 region = this.getRegion(r);
43498 // tried using nb[r], but it does not work..
43500 region.showPanel(abn[r]);
43511 factory : function(cfg)
43514 var validRegions = Roo.bootstrap.layout.Border.regions;
43516 var target = cfg.region;
43519 var r = Roo.bootstrap.layout;
43523 return new r.North(cfg);
43525 return new r.South(cfg);
43527 return new r.East(cfg);
43529 return new r.West(cfg);
43531 return new r.Center(cfg);
43533 throw 'Layout region "'+target+'" not supported.';
43540 * Ext JS Library 1.1.1
43541 * Copyright(c) 2006-2007, Ext JS, LLC.
43543 * Originally Released Under LGPL - original licence link has changed is not relivant.
43546 * <script type="text/javascript">
43550 * @class Roo.bootstrap.layout.Basic
43551 * @extends Roo.util.Observable
43552 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43553 * and does not have a titlebar, tabs or any other features. All it does is size and position
43554 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43555 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43556 * @cfg {string} region the region that it inhabits..
43557 * @cfg {bool} skipConfig skip config?
43561 Roo.bootstrap.layout.Basic = function(config){
43563 this.mgr = config.mgr;
43565 this.position = config.region;
43567 var skipConfig = config.skipConfig;
43571 * @scope Roo.BasicLayoutRegion
43575 * @event beforeremove
43576 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43577 * @param {Roo.LayoutRegion} this
43578 * @param {Roo.ContentPanel} panel The panel
43579 * @param {Object} e The cancel event object
43581 "beforeremove" : true,
43583 * @event invalidated
43584 * Fires when the layout for this region is changed.
43585 * @param {Roo.LayoutRegion} this
43587 "invalidated" : true,
43589 * @event visibilitychange
43590 * Fires when this region is shown or hidden
43591 * @param {Roo.LayoutRegion} this
43592 * @param {Boolean} visibility true or false
43594 "visibilitychange" : true,
43596 * @event paneladded
43597 * Fires when a panel is added.
43598 * @param {Roo.LayoutRegion} this
43599 * @param {Roo.ContentPanel} panel The panel
43601 "paneladded" : true,
43603 * @event panelremoved
43604 * Fires when a panel is removed.
43605 * @param {Roo.LayoutRegion} this
43606 * @param {Roo.ContentPanel} panel The panel
43608 "panelremoved" : true,
43610 * @event beforecollapse
43611 * Fires when this region before collapse.
43612 * @param {Roo.LayoutRegion} this
43614 "beforecollapse" : true,
43617 * Fires when this region is collapsed.
43618 * @param {Roo.LayoutRegion} this
43620 "collapsed" : true,
43623 * Fires when this region is expanded.
43624 * @param {Roo.LayoutRegion} this
43629 * Fires when this region is slid into view.
43630 * @param {Roo.LayoutRegion} this
43632 "slideshow" : true,
43635 * Fires when this region slides out of view.
43636 * @param {Roo.LayoutRegion} this
43638 "slidehide" : true,
43640 * @event panelactivated
43641 * Fires when a panel is activated.
43642 * @param {Roo.LayoutRegion} this
43643 * @param {Roo.ContentPanel} panel The activated panel
43645 "panelactivated" : true,
43648 * Fires when the user resizes this region.
43649 * @param {Roo.LayoutRegion} this
43650 * @param {Number} newSize The new size (width for east/west, height for north/south)
43654 /** A collection of panels in this region. @type Roo.util.MixedCollection */
43655 this.panels = new Roo.util.MixedCollection();
43656 this.panels.getKey = this.getPanelId.createDelegate(this);
43658 this.activePanel = null;
43659 // ensure listeners are added...
43661 if (config.listeners || config.events) {
43662 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43663 listeners : config.listeners || {},
43664 events : config.events || {}
43668 if(skipConfig !== true){
43669 this.applyConfig(config);
43673 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43675 getPanelId : function(p){
43679 applyConfig : function(config){
43680 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43681 this.config = config;
43686 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
43687 * the width, for horizontal (north, south) the height.
43688 * @param {Number} newSize The new width or height
43690 resizeTo : function(newSize){
43691 var el = this.el ? this.el :
43692 (this.activePanel ? this.activePanel.getEl() : null);
43694 switch(this.position){
43697 el.setWidth(newSize);
43698 this.fireEvent("resized", this, newSize);
43702 el.setHeight(newSize);
43703 this.fireEvent("resized", this, newSize);
43709 getBox : function(){
43710 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43713 getMargins : function(){
43714 return this.margins;
43717 updateBox : function(box){
43719 var el = this.activePanel.getEl();
43720 el.dom.style.left = box.x + "px";
43721 el.dom.style.top = box.y + "px";
43722 this.activePanel.setSize(box.width, box.height);
43726 * Returns the container element for this region.
43727 * @return {Roo.Element}
43729 getEl : function(){
43730 return this.activePanel;
43734 * Returns true if this region is currently visible.
43735 * @return {Boolean}
43737 isVisible : function(){
43738 return this.activePanel ? true : false;
43741 setActivePanel : function(panel){
43742 panel = this.getPanel(panel);
43743 if(this.activePanel && this.activePanel != panel){
43744 this.activePanel.setActiveState(false);
43745 this.activePanel.getEl().setLeftTop(-10000,-10000);
43747 this.activePanel = panel;
43748 panel.setActiveState(true);
43750 panel.setSize(this.box.width, this.box.height);
43752 this.fireEvent("panelactivated", this, panel);
43753 this.fireEvent("invalidated");
43757 * Show the specified panel.
43758 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43759 * @return {Roo.ContentPanel} The shown panel or null
43761 showPanel : function(panel){
43762 panel = this.getPanel(panel);
43764 this.setActivePanel(panel);
43770 * Get the active panel for this region.
43771 * @return {Roo.ContentPanel} The active panel or null
43773 getActivePanel : function(){
43774 return this.activePanel;
43778 * Add the passed ContentPanel(s)
43779 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43780 * @return {Roo.ContentPanel} The panel added (if only one was added)
43782 add : function(panel){
43783 if(arguments.length > 1){
43784 for(var i = 0, len = arguments.length; i < len; i++) {
43785 this.add(arguments[i]);
43789 if(this.hasPanel(panel)){
43790 this.showPanel(panel);
43793 var el = panel.getEl();
43794 if(el.dom.parentNode != this.mgr.el.dom){
43795 this.mgr.el.dom.appendChild(el.dom);
43797 if(panel.setRegion){
43798 panel.setRegion(this);
43800 this.panels.add(panel);
43801 el.setStyle("position", "absolute");
43802 if(!panel.background){
43803 this.setActivePanel(panel);
43804 if(this.config.initialSize && this.panels.getCount()==1){
43805 this.resizeTo(this.config.initialSize);
43808 this.fireEvent("paneladded", this, panel);
43813 * Returns true if the panel is in this region.
43814 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43815 * @return {Boolean}
43817 hasPanel : function(panel){
43818 if(typeof panel == "object"){ // must be panel obj
43819 panel = panel.getId();
43821 return this.getPanel(panel) ? true : false;
43825 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43826 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43827 * @param {Boolean} preservePanel Overrides the config preservePanel option
43828 * @return {Roo.ContentPanel} The panel that was removed
43830 remove : function(panel, preservePanel){
43831 panel = this.getPanel(panel);
43836 this.fireEvent("beforeremove", this, panel, e);
43837 if(e.cancel === true){
43840 var panelId = panel.getId();
43841 this.panels.removeKey(panelId);
43846 * Returns the panel specified or null if it's not in this region.
43847 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43848 * @return {Roo.ContentPanel}
43850 getPanel : function(id){
43851 if(typeof id == "object"){ // must be panel obj
43854 return this.panels.get(id);
43858 * Returns this regions position (north/south/east/west/center).
43861 getPosition: function(){
43862 return this.position;
43866 * Ext JS Library 1.1.1
43867 * Copyright(c) 2006-2007, Ext JS, LLC.
43869 * Originally Released Under LGPL - original licence link has changed is not relivant.
43872 * <script type="text/javascript">
43876 * @class Roo.bootstrap.layout.Region
43877 * @extends Roo.bootstrap.layout.Basic
43878 * This class represents a region in a layout manager.
43880 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43881 * @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})
43882 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43883 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43884 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43885 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43886 * @cfg {String} title The title for the region (overrides panel titles)
43887 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43888 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43889 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43890 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43891 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43892 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43893 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43894 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43895 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43896 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43898 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43899 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43900 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43901 * @cfg {Number} width For East/West panels
43902 * @cfg {Number} height For North/South panels
43903 * @cfg {Boolean} split To show the splitter
43904 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43906 * @cfg {string} cls Extra CSS classes to add to region
43908 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43909 * @cfg {string} region the region that it inhabits..
43912 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43913 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43915 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43916 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43917 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43919 Roo.bootstrap.layout.Region = function(config)
43921 this.applyConfig(config);
43923 var mgr = config.mgr;
43924 var pos = config.region;
43925 config.skipConfig = true;
43926 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43929 this.onRender(mgr.el);
43932 this.visible = true;
43933 this.collapsed = false;
43934 this.unrendered_panels = [];
43937 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43939 position: '', // set by wrapper (eg. north/south etc..)
43940 unrendered_panels : null, // unrendered panels.
43942 tabPosition : false,
43944 mgr: false, // points to 'Border'
43947 createBody : function(){
43948 /** This region's body element
43949 * @type Roo.Element */
43950 this.bodyEl = this.el.createChild({
43952 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43956 onRender: function(ctr, pos)
43958 var dh = Roo.DomHelper;
43959 /** This region's container element
43960 * @type Roo.Element */
43961 this.el = dh.append(ctr.dom, {
43963 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43965 /** This region's title element
43966 * @type Roo.Element */
43968 this.titleEl = dh.append(this.el.dom, {
43970 unselectable: "on",
43971 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43973 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43974 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43978 this.titleEl.enableDisplayMode();
43979 /** This region's title text element
43980 * @type HTMLElement */
43981 this.titleTextEl = this.titleEl.dom.firstChild;
43982 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43984 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43985 this.closeBtn.enableDisplayMode();
43986 this.closeBtn.on("click", this.closeClicked, this);
43987 this.closeBtn.hide();
43989 this.createBody(this.config);
43990 if(this.config.hideWhenEmpty){
43992 this.on("paneladded", this.validateVisibility, this);
43993 this.on("panelremoved", this.validateVisibility, this);
43995 if(this.autoScroll){
43996 this.bodyEl.setStyle("overflow", "auto");
43998 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44000 //if(c.titlebar !== false){
44001 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44002 this.titleEl.hide();
44004 this.titleEl.show();
44005 if(this.config.title){
44006 this.titleTextEl.innerHTML = this.config.title;
44010 if(this.config.collapsed){
44011 this.collapse(true);
44013 if(this.config.hidden){
44017 if (this.unrendered_panels && this.unrendered_panels.length) {
44018 for (var i =0;i< this.unrendered_panels.length; i++) {
44019 this.add(this.unrendered_panels[i]);
44021 this.unrendered_panels = null;
44027 applyConfig : function(c)
44030 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44031 var dh = Roo.DomHelper;
44032 if(c.titlebar !== false){
44033 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44034 this.collapseBtn.on("click", this.collapse, this);
44035 this.collapseBtn.enableDisplayMode();
44037 if(c.showPin === true || this.showPin){
44038 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44039 this.stickBtn.enableDisplayMode();
44040 this.stickBtn.on("click", this.expand, this);
44041 this.stickBtn.hide();
44046 /** This region's collapsed element
44047 * @type Roo.Element */
44050 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44051 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44054 if(c.floatable !== false){
44055 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44056 this.collapsedEl.on("click", this.collapseClick, this);
44059 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44060 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44061 id: "message", unselectable: "on", style:{"float":"left"}});
44062 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44064 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44065 this.expandBtn.on("click", this.expand, this);
44069 if(this.collapseBtn){
44070 this.collapseBtn.setVisible(c.collapsible == true);
44073 this.cmargins = c.cmargins || this.cmargins ||
44074 (this.position == "west" || this.position == "east" ?
44075 {top: 0, left: 2, right:2, bottom: 0} :
44076 {top: 2, left: 0, right:0, bottom: 2});
44078 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44081 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44083 this.autoScroll = c.autoScroll || false;
44088 this.duration = c.duration || .30;
44089 this.slideDuration = c.slideDuration || .45;
44094 * Returns true if this region is currently visible.
44095 * @return {Boolean}
44097 isVisible : function(){
44098 return this.visible;
44102 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44103 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
44105 //setCollapsedTitle : function(title){
44106 // title = title || " ";
44107 // if(this.collapsedTitleTextEl){
44108 // this.collapsedTitleTextEl.innerHTML = title;
44112 getBox : function(){
44114 // if(!this.collapsed){
44115 b = this.el.getBox(false, true);
44117 // b = this.collapsedEl.getBox(false, true);
44122 getMargins : function(){
44123 return this.margins;
44124 //return this.collapsed ? this.cmargins : this.margins;
44127 highlight : function(){
44128 this.el.addClass("x-layout-panel-dragover");
44131 unhighlight : function(){
44132 this.el.removeClass("x-layout-panel-dragover");
44135 updateBox : function(box)
44137 if (!this.bodyEl) {
44138 return; // not rendered yet..
44142 if(!this.collapsed){
44143 this.el.dom.style.left = box.x + "px";
44144 this.el.dom.style.top = box.y + "px";
44145 this.updateBody(box.width, box.height);
44147 this.collapsedEl.dom.style.left = box.x + "px";
44148 this.collapsedEl.dom.style.top = box.y + "px";
44149 this.collapsedEl.setSize(box.width, box.height);
44152 this.tabs.autoSizeTabs();
44156 updateBody : function(w, h)
44159 this.el.setWidth(w);
44160 w -= this.el.getBorderWidth("rl");
44161 if(this.config.adjustments){
44162 w += this.config.adjustments[0];
44165 if(h !== null && h > 0){
44166 this.el.setHeight(h);
44167 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44168 h -= this.el.getBorderWidth("tb");
44169 if(this.config.adjustments){
44170 h += this.config.adjustments[1];
44172 this.bodyEl.setHeight(h);
44174 h = this.tabs.syncHeight(h);
44177 if(this.panelSize){
44178 w = w !== null ? w : this.panelSize.width;
44179 h = h !== null ? h : this.panelSize.height;
44181 if(this.activePanel){
44182 var el = this.activePanel.getEl();
44183 w = w !== null ? w : el.getWidth();
44184 h = h !== null ? h : el.getHeight();
44185 this.panelSize = {width: w, height: h};
44186 this.activePanel.setSize(w, h);
44188 if(Roo.isIE && this.tabs){
44189 this.tabs.el.repaint();
44194 * Returns the container element for this region.
44195 * @return {Roo.Element}
44197 getEl : function(){
44202 * Hides this region.
44205 //if(!this.collapsed){
44206 this.el.dom.style.left = "-2000px";
44209 // this.collapsedEl.dom.style.left = "-2000px";
44210 // this.collapsedEl.hide();
44212 this.visible = false;
44213 this.fireEvent("visibilitychange", this, false);
44217 * Shows this region if it was previously hidden.
44220 //if(!this.collapsed){
44223 // this.collapsedEl.show();
44225 this.visible = true;
44226 this.fireEvent("visibilitychange", this, true);
44229 closeClicked : function(){
44230 if(this.activePanel){
44231 this.remove(this.activePanel);
44235 collapseClick : function(e){
44237 e.stopPropagation();
44240 e.stopPropagation();
44246 * Collapses this region.
44247 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44250 collapse : function(skipAnim, skipCheck = false){
44251 if(this.collapsed) {
44255 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44257 this.collapsed = true;
44259 this.split.el.hide();
44261 if(this.config.animate && skipAnim !== true){
44262 this.fireEvent("invalidated", this);
44263 this.animateCollapse();
44265 this.el.setLocation(-20000,-20000);
44267 this.collapsedEl.show();
44268 this.fireEvent("collapsed", this);
44269 this.fireEvent("invalidated", this);
44275 animateCollapse : function(){
44280 * Expands this region if it was previously collapsed.
44281 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44282 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44285 expand : function(e, skipAnim){
44287 e.stopPropagation();
44289 if(!this.collapsed || this.el.hasActiveFx()) {
44293 this.afterSlideIn();
44296 this.collapsed = false;
44297 if(this.config.animate && skipAnim !== true){
44298 this.animateExpand();
44302 this.split.el.show();
44304 this.collapsedEl.setLocation(-2000,-2000);
44305 this.collapsedEl.hide();
44306 this.fireEvent("invalidated", this);
44307 this.fireEvent("expanded", this);
44311 animateExpand : function(){
44315 initTabs : function()
44317 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44319 var ts = new Roo.bootstrap.panel.Tabs({
44320 el: this.bodyEl.dom,
44322 tabPosition: this.tabPosition ? this.tabPosition : 'top',
44323 disableTooltips: this.config.disableTabTips,
44324 toolbar : this.config.toolbar
44327 if(this.config.hideTabs){
44328 ts.stripWrap.setDisplayed(false);
44331 ts.resizeTabs = this.config.resizeTabs === true;
44332 ts.minTabWidth = this.config.minTabWidth || 40;
44333 ts.maxTabWidth = this.config.maxTabWidth || 250;
44334 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44335 ts.monitorResize = false;
44336 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44337 ts.bodyEl.addClass('roo-layout-tabs-body');
44338 this.panels.each(this.initPanelAsTab, this);
44341 initPanelAsTab : function(panel){
44342 var ti = this.tabs.addTab(
44346 this.config.closeOnTab && panel.isClosable(),
44349 if(panel.tabTip !== undefined){
44350 ti.setTooltip(panel.tabTip);
44352 ti.on("activate", function(){
44353 this.setActivePanel(panel);
44356 if(this.config.closeOnTab){
44357 ti.on("beforeclose", function(t, e){
44359 this.remove(panel);
44363 panel.tabItem = ti;
44368 updatePanelTitle : function(panel, title)
44370 if(this.activePanel == panel){
44371 this.updateTitle(title);
44374 var ti = this.tabs.getTab(panel.getEl().id);
44376 if(panel.tabTip !== undefined){
44377 ti.setTooltip(panel.tabTip);
44382 updateTitle : function(title){
44383 if(this.titleTextEl && !this.config.title){
44384 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
44388 setActivePanel : function(panel)
44390 panel = this.getPanel(panel);
44391 if(this.activePanel && this.activePanel != panel){
44392 if(this.activePanel.setActiveState(false) === false){
44396 this.activePanel = panel;
44397 panel.setActiveState(true);
44398 if(this.panelSize){
44399 panel.setSize(this.panelSize.width, this.panelSize.height);
44402 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44404 this.updateTitle(panel.getTitle());
44406 this.fireEvent("invalidated", this);
44408 this.fireEvent("panelactivated", this, panel);
44412 * Shows the specified panel.
44413 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44414 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44416 showPanel : function(panel)
44418 panel = this.getPanel(panel);
44421 var tab = this.tabs.getTab(panel.getEl().id);
44422 if(tab.isHidden()){
44423 this.tabs.unhideTab(tab.id);
44427 this.setActivePanel(panel);
44434 * Get the active panel for this region.
44435 * @return {Roo.ContentPanel} The active panel or null
44437 getActivePanel : function(){
44438 return this.activePanel;
44441 validateVisibility : function(){
44442 if(this.panels.getCount() < 1){
44443 this.updateTitle(" ");
44444 this.closeBtn.hide();
44447 if(!this.isVisible()){
44454 * Adds the passed ContentPanel(s) to this region.
44455 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44456 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44458 add : function(panel)
44460 if(arguments.length > 1){
44461 for(var i = 0, len = arguments.length; i < len; i++) {
44462 this.add(arguments[i]);
44467 // if we have not been rendered yet, then we can not really do much of this..
44468 if (!this.bodyEl) {
44469 this.unrendered_panels.push(panel);
44476 if(this.hasPanel(panel)){
44477 this.showPanel(panel);
44480 panel.setRegion(this);
44481 this.panels.add(panel);
44482 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44483 // sinle panel - no tab...?? would it not be better to render it with the tabs,
44484 // and hide them... ???
44485 this.bodyEl.dom.appendChild(panel.getEl().dom);
44486 if(panel.background !== true){
44487 this.setActivePanel(panel);
44489 this.fireEvent("paneladded", this, panel);
44496 this.initPanelAsTab(panel);
44500 if(panel.background !== true){
44501 this.tabs.activate(panel.getEl().id);
44503 this.fireEvent("paneladded", this, panel);
44508 * Hides the tab for the specified panel.
44509 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44511 hidePanel : function(panel){
44512 if(this.tabs && (panel = this.getPanel(panel))){
44513 this.tabs.hideTab(panel.getEl().id);
44518 * Unhides the tab for a previously hidden panel.
44519 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44521 unhidePanel : function(panel){
44522 if(this.tabs && (panel = this.getPanel(panel))){
44523 this.tabs.unhideTab(panel.getEl().id);
44527 clearPanels : function(){
44528 while(this.panels.getCount() > 0){
44529 this.remove(this.panels.first());
44534 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44535 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44536 * @param {Boolean} preservePanel Overrides the config preservePanel option
44537 * @return {Roo.ContentPanel} The panel that was removed
44539 remove : function(panel, preservePanel)
44541 panel = this.getPanel(panel);
44546 this.fireEvent("beforeremove", this, panel, e);
44547 if(e.cancel === true){
44550 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44551 var panelId = panel.getId();
44552 this.panels.removeKey(panelId);
44554 document.body.appendChild(panel.getEl().dom);
44557 this.tabs.removeTab(panel.getEl().id);
44558 }else if (!preservePanel){
44559 this.bodyEl.dom.removeChild(panel.getEl().dom);
44561 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44562 var p = this.panels.first();
44563 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44564 tempEl.appendChild(p.getEl().dom);
44565 this.bodyEl.update("");
44566 this.bodyEl.dom.appendChild(p.getEl().dom);
44568 this.updateTitle(p.getTitle());
44570 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44571 this.setActivePanel(p);
44573 panel.setRegion(null);
44574 if(this.activePanel == panel){
44575 this.activePanel = null;
44577 if(this.config.autoDestroy !== false && preservePanel !== true){
44578 try{panel.destroy();}catch(e){}
44580 this.fireEvent("panelremoved", this, panel);
44585 * Returns the TabPanel component used by this region
44586 * @return {Roo.TabPanel}
44588 getTabs : function(){
44592 createTool : function(parentEl, className){
44593 var btn = Roo.DomHelper.append(parentEl, {
44595 cls: "x-layout-tools-button",
44598 cls: "roo-layout-tools-button-inner " + className,
44602 btn.addClassOnOver("roo-layout-tools-button-over");
44607 * Ext JS Library 1.1.1
44608 * Copyright(c) 2006-2007, Ext JS, LLC.
44610 * Originally Released Under LGPL - original licence link has changed is not relivant.
44613 * <script type="text/javascript">
44619 * @class Roo.SplitLayoutRegion
44620 * @extends Roo.LayoutRegion
44621 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44623 Roo.bootstrap.layout.Split = function(config){
44624 this.cursor = config.cursor;
44625 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44628 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44630 splitTip : "Drag to resize.",
44631 collapsibleSplitTip : "Drag to resize. Double click to hide.",
44632 useSplitTips : false,
44634 applyConfig : function(config){
44635 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44638 onRender : function(ctr,pos) {
44640 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44641 if(!this.config.split){
44646 var splitEl = Roo.DomHelper.append(ctr.dom, {
44648 id: this.el.id + "-split",
44649 cls: "roo-layout-split roo-layout-split-"+this.position,
44652 /** The SplitBar for this region
44653 * @type Roo.SplitBar */
44654 // does not exist yet...
44655 Roo.log([this.position, this.orientation]);
44657 this.split = new Roo.bootstrap.SplitBar({
44658 dragElement : splitEl,
44659 resizingElement: this.el,
44660 orientation : this.orientation
44663 this.split.on("moved", this.onSplitMove, this);
44664 this.split.useShim = this.config.useShim === true;
44665 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44666 if(this.useSplitTips){
44667 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44669 //if(config.collapsible){
44670 // this.split.el.on("dblclick", this.collapse, this);
44673 if(typeof this.config.minSize != "undefined"){
44674 this.split.minSize = this.config.minSize;
44676 if(typeof this.config.maxSize != "undefined"){
44677 this.split.maxSize = this.config.maxSize;
44679 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44680 this.hideSplitter();
44685 getHMaxSize : function(){
44686 var cmax = this.config.maxSize || 10000;
44687 var center = this.mgr.getRegion("center");
44688 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44691 getVMaxSize : function(){
44692 var cmax = this.config.maxSize || 10000;
44693 var center = this.mgr.getRegion("center");
44694 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44697 onSplitMove : function(split, newSize){
44698 this.fireEvent("resized", this, newSize);
44702 * Returns the {@link Roo.SplitBar} for this region.
44703 * @return {Roo.SplitBar}
44705 getSplitBar : function(){
44710 this.hideSplitter();
44711 Roo.bootstrap.layout.Split.superclass.hide.call(this);
44714 hideSplitter : function(){
44716 this.split.el.setLocation(-2000,-2000);
44717 this.split.el.hide();
44723 this.split.el.show();
44725 Roo.bootstrap.layout.Split.superclass.show.call(this);
44728 beforeSlide: function(){
44729 if(Roo.isGecko){// firefox overflow auto bug workaround
44730 this.bodyEl.clip();
44732 this.tabs.bodyEl.clip();
44734 if(this.activePanel){
44735 this.activePanel.getEl().clip();
44737 if(this.activePanel.beforeSlide){
44738 this.activePanel.beforeSlide();
44744 afterSlide : function(){
44745 if(Roo.isGecko){// firefox overflow auto bug workaround
44746 this.bodyEl.unclip();
44748 this.tabs.bodyEl.unclip();
44750 if(this.activePanel){
44751 this.activePanel.getEl().unclip();
44752 if(this.activePanel.afterSlide){
44753 this.activePanel.afterSlide();
44759 initAutoHide : function(){
44760 if(this.autoHide !== false){
44761 if(!this.autoHideHd){
44762 var st = new Roo.util.DelayedTask(this.slideIn, this);
44763 this.autoHideHd = {
44764 "mouseout": function(e){
44765 if(!e.within(this.el, true)){
44769 "mouseover" : function(e){
44775 this.el.on(this.autoHideHd);
44779 clearAutoHide : function(){
44780 if(this.autoHide !== false){
44781 this.el.un("mouseout", this.autoHideHd.mouseout);
44782 this.el.un("mouseover", this.autoHideHd.mouseover);
44786 clearMonitor : function(){
44787 Roo.get(document).un("click", this.slideInIf, this);
44790 // these names are backwards but not changed for compat
44791 slideOut : function(){
44792 if(this.isSlid || this.el.hasActiveFx()){
44795 this.isSlid = true;
44796 if(this.collapseBtn){
44797 this.collapseBtn.hide();
44799 this.closeBtnState = this.closeBtn.getStyle('display');
44800 this.closeBtn.hide();
44802 this.stickBtn.show();
44805 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44806 this.beforeSlide();
44807 this.el.setStyle("z-index", 10001);
44808 this.el.slideIn(this.getSlideAnchor(), {
44809 callback: function(){
44811 this.initAutoHide();
44812 Roo.get(document).on("click", this.slideInIf, this);
44813 this.fireEvent("slideshow", this);
44820 afterSlideIn : function(){
44821 this.clearAutoHide();
44822 this.isSlid = false;
44823 this.clearMonitor();
44824 this.el.setStyle("z-index", "");
44825 if(this.collapseBtn){
44826 this.collapseBtn.show();
44828 this.closeBtn.setStyle('display', this.closeBtnState);
44830 this.stickBtn.hide();
44832 this.fireEvent("slidehide", this);
44835 slideIn : function(cb){
44836 if(!this.isSlid || this.el.hasActiveFx()){
44840 this.isSlid = false;
44841 this.beforeSlide();
44842 this.el.slideOut(this.getSlideAnchor(), {
44843 callback: function(){
44844 this.el.setLeftTop(-10000, -10000);
44846 this.afterSlideIn();
44854 slideInIf : function(e){
44855 if(!e.within(this.el)){
44860 animateCollapse : function(){
44861 this.beforeSlide();
44862 this.el.setStyle("z-index", 20000);
44863 var anchor = this.getSlideAnchor();
44864 this.el.slideOut(anchor, {
44865 callback : function(){
44866 this.el.setStyle("z-index", "");
44867 this.collapsedEl.slideIn(anchor, {duration:.3});
44869 this.el.setLocation(-10000,-10000);
44871 this.fireEvent("collapsed", this);
44878 animateExpand : function(){
44879 this.beforeSlide();
44880 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44881 this.el.setStyle("z-index", 20000);
44882 this.collapsedEl.hide({
44885 this.el.slideIn(this.getSlideAnchor(), {
44886 callback : function(){
44887 this.el.setStyle("z-index", "");
44890 this.split.el.show();
44892 this.fireEvent("invalidated", this);
44893 this.fireEvent("expanded", this);
44921 getAnchor : function(){
44922 return this.anchors[this.position];
44925 getCollapseAnchor : function(){
44926 return this.canchors[this.position];
44929 getSlideAnchor : function(){
44930 return this.sanchors[this.position];
44933 getAlignAdj : function(){
44934 var cm = this.cmargins;
44935 switch(this.position){
44951 getExpandAdj : function(){
44952 var c = this.collapsedEl, cm = this.cmargins;
44953 switch(this.position){
44955 return [-(cm.right+c.getWidth()+cm.left), 0];
44958 return [cm.right+c.getWidth()+cm.left, 0];
44961 return [0, -(cm.top+cm.bottom+c.getHeight())];
44964 return [0, cm.top+cm.bottom+c.getHeight()];
44970 * Ext JS Library 1.1.1
44971 * Copyright(c) 2006-2007, Ext JS, LLC.
44973 * Originally Released Under LGPL - original licence link has changed is not relivant.
44976 * <script type="text/javascript">
44979 * These classes are private internal classes
44981 Roo.bootstrap.layout.Center = function(config){
44982 config.region = "center";
44983 Roo.bootstrap.layout.Region.call(this, config);
44984 this.visible = true;
44985 this.minWidth = config.minWidth || 20;
44986 this.minHeight = config.minHeight || 20;
44989 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44991 // center panel can't be hidden
44995 // center panel can't be hidden
44998 getMinWidth: function(){
44999 return this.minWidth;
45002 getMinHeight: function(){
45003 return this.minHeight;
45017 Roo.bootstrap.layout.North = function(config)
45019 config.region = 'north';
45020 config.cursor = 'n-resize';
45022 Roo.bootstrap.layout.Split.call(this, config);
45026 this.split.placement = Roo.bootstrap.SplitBar.TOP;
45027 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45028 this.split.el.addClass("roo-layout-split-v");
45030 //var size = config.initialSize || config.height;
45031 //if(this.el && typeof size != "undefined"){
45032 // this.el.setHeight(size);
45035 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45037 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45040 onRender : function(ctr, pos)
45042 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45043 var size = this.config.initialSize || this.config.height;
45044 if(this.el && typeof size != "undefined"){
45045 this.el.setHeight(size);
45050 getBox : function(){
45051 if(this.collapsed){
45052 return this.collapsedEl.getBox();
45054 var box = this.el.getBox();
45056 box.height += this.split.el.getHeight();
45061 updateBox : function(box){
45062 if(this.split && !this.collapsed){
45063 box.height -= this.split.el.getHeight();
45064 this.split.el.setLeft(box.x);
45065 this.split.el.setTop(box.y+box.height);
45066 this.split.el.setWidth(box.width);
45068 if(this.collapsed){
45069 this.updateBody(box.width, null);
45071 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45079 Roo.bootstrap.layout.South = function(config){
45080 config.region = 'south';
45081 config.cursor = 's-resize';
45082 Roo.bootstrap.layout.Split.call(this, config);
45084 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45085 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45086 this.split.el.addClass("roo-layout-split-v");
45091 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45092 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45094 onRender : function(ctr, pos)
45096 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45097 var size = this.config.initialSize || this.config.height;
45098 if(this.el && typeof size != "undefined"){
45099 this.el.setHeight(size);
45104 getBox : function(){
45105 if(this.collapsed){
45106 return this.collapsedEl.getBox();
45108 var box = this.el.getBox();
45110 var sh = this.split.el.getHeight();
45117 updateBox : function(box){
45118 if(this.split && !this.collapsed){
45119 var sh = this.split.el.getHeight();
45122 this.split.el.setLeft(box.x);
45123 this.split.el.setTop(box.y-sh);
45124 this.split.el.setWidth(box.width);
45126 if(this.collapsed){
45127 this.updateBody(box.width, null);
45129 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45133 Roo.bootstrap.layout.East = function(config){
45134 config.region = "east";
45135 config.cursor = "e-resize";
45136 Roo.bootstrap.layout.Split.call(this, config);
45138 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45139 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45140 this.split.el.addClass("roo-layout-split-h");
45144 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45145 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45147 onRender : function(ctr, pos)
45149 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45150 var size = this.config.initialSize || this.config.width;
45151 if(this.el && typeof size != "undefined"){
45152 this.el.setWidth(size);
45157 getBox : function(){
45158 if(this.collapsed){
45159 return this.collapsedEl.getBox();
45161 var box = this.el.getBox();
45163 var sw = this.split.el.getWidth();
45170 updateBox : function(box){
45171 if(this.split && !this.collapsed){
45172 var sw = this.split.el.getWidth();
45174 this.split.el.setLeft(box.x);
45175 this.split.el.setTop(box.y);
45176 this.split.el.setHeight(box.height);
45179 if(this.collapsed){
45180 this.updateBody(null, box.height);
45182 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45186 Roo.bootstrap.layout.West = function(config){
45187 config.region = "west";
45188 config.cursor = "w-resize";
45190 Roo.bootstrap.layout.Split.call(this, config);
45192 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45193 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45194 this.split.el.addClass("roo-layout-split-h");
45198 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45199 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45201 onRender: function(ctr, pos)
45203 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45204 var size = this.config.initialSize || this.config.width;
45205 if(typeof size != "undefined"){
45206 this.el.setWidth(size);
45210 getBox : function(){
45211 if(this.collapsed){
45212 return this.collapsedEl.getBox();
45214 var box = this.el.getBox();
45215 if (box.width == 0) {
45216 box.width = this.config.width; // kludge?
45219 box.width += this.split.el.getWidth();
45224 updateBox : function(box){
45225 if(this.split && !this.collapsed){
45226 var sw = this.split.el.getWidth();
45228 this.split.el.setLeft(box.x+box.width);
45229 this.split.el.setTop(box.y);
45230 this.split.el.setHeight(box.height);
45232 if(this.collapsed){
45233 this.updateBody(null, box.height);
45235 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45239 * Ext JS Library 1.1.1
45240 * Copyright(c) 2006-2007, Ext JS, LLC.
45242 * Originally Released Under LGPL - original licence link has changed is not relivant.
45245 * <script type="text/javascript">
45248 * @class Roo.bootstrap.paenl.Content
45249 * @extends Roo.util.Observable
45250 * @children Roo.bootstrap.Component
45251 * @parent builder Roo.bootstrap.layout.Border
45252 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45253 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
45254 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
45255 * @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
45256 * @cfg {Boolean} closable True if the panel can be closed/removed
45257 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45258 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45259 * @cfg {Toolbar} toolbar A toolbar for this panel
45260 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45261 * @cfg {String} title The title for this panel
45262 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45263 * @cfg {String} url Calls {@link #setUrl} with this value
45264 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45265 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45266 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45267 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
45268 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
45269 * @cfg {Boolean} badges render the badges
45270 * @cfg {String} cls extra classes to use
45271 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45274 * Create a new ContentPanel.
45275 * @param {String/Object} config A string to set only the title or a config object
45278 Roo.bootstrap.panel.Content = function( config){
45280 this.tpl = config.tpl || false;
45282 var el = config.el;
45283 var content = config.content;
45285 if(config.autoCreate){ // xtype is available if this is called from factory
45288 this.el = Roo.get(el);
45289 if(!this.el && config && config.autoCreate){
45290 if(typeof config.autoCreate == "object"){
45291 if(!config.autoCreate.id){
45292 config.autoCreate.id = config.id||el;
45294 this.el = Roo.DomHelper.append(document.body,
45295 config.autoCreate, true);
45299 cls: (config.cls || '') +
45300 (config.background ? ' bg-' + config.background : '') +
45301 " roo-layout-inactive-content",
45304 if (config.iframe) {
45308 style : 'border: 0px',
45309 src : 'about:blank'
45315 elcfg.html = config.html;
45319 this.el = Roo.DomHelper.append(document.body, elcfg , true);
45320 if (config.iframe) {
45321 this.iframeEl = this.el.select('iframe',true).first();
45326 this.closable = false;
45327 this.loaded = false;
45328 this.active = false;
45331 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45333 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45335 this.wrapEl = this.el; //this.el.wrap();
45337 if (config.toolbar.items) {
45338 ti = config.toolbar.items ;
45339 delete config.toolbar.items ;
45343 this.toolbar.render(this.wrapEl, 'before');
45344 for(var i =0;i < ti.length;i++) {
45345 // Roo.log(['add child', items[i]]);
45346 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45348 this.toolbar.items = nitems;
45349 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45350 delete config.toolbar;
45354 // xtype created footer. - not sure if will work as we normally have to render first..
45355 if (this.footer && !this.footer.el && this.footer.xtype) {
45356 if (!this.wrapEl) {
45357 this.wrapEl = this.el.wrap();
45360 this.footer.container = this.wrapEl.createChild();
45362 this.footer = Roo.factory(this.footer, Roo);
45367 if(typeof config == "string"){
45368 this.title = config;
45370 Roo.apply(this, config);
45374 this.resizeEl = Roo.get(this.resizeEl, true);
45376 this.resizeEl = this.el;
45378 // handle view.xtype
45386 * Fires when this panel is activated.
45387 * @param {Roo.ContentPanel} this
45391 * @event deactivate
45392 * Fires when this panel is activated.
45393 * @param {Roo.ContentPanel} this
45395 "deactivate" : true,
45399 * Fires when this panel is resized if fitToFrame is true.
45400 * @param {Roo.ContentPanel} this
45401 * @param {Number} width The width after any component adjustments
45402 * @param {Number} height The height after any component adjustments
45408 * Fires when this tab is created
45409 * @param {Roo.ContentPanel} this
45415 * Fires when this content is scrolled
45416 * @param {Roo.ContentPanel} this
45417 * @param {Event} scrollEvent
45428 if(this.autoScroll && !this.iframe){
45429 this.resizeEl.setStyle("overflow", "auto");
45430 this.resizeEl.on('scroll', this.onScroll, this);
45432 // fix randome scrolling
45433 //this.el.on('scroll', function() {
45434 // Roo.log('fix random scolling');
45435 // this.scrollTo('top',0);
45438 content = content || this.content;
45440 this.setContent(content);
45442 if(config && config.url){
45443 this.setUrl(this.url, this.params, this.loadOnce);
45448 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45450 if (this.view && typeof(this.view.xtype) != 'undefined') {
45451 this.view.el = this.el.appendChild(document.createElement("div"));
45452 this.view = Roo.factory(this.view);
45453 this.view.render && this.view.render(false, '');
45457 this.fireEvent('render', this);
45460 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45470 /* Resize Element - use this to work out scroll etc. */
45473 setRegion : function(region){
45474 this.region = region;
45475 this.setActiveClass(region && !this.background);
45479 setActiveClass: function(state)
45482 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45483 this.el.setStyle('position','relative');
45485 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45486 this.el.setStyle('position', 'absolute');
45491 * Returns the toolbar for this Panel if one was configured.
45492 * @return {Roo.Toolbar}
45494 getToolbar : function(){
45495 return this.toolbar;
45498 setActiveState : function(active)
45500 this.active = active;
45501 this.setActiveClass(active);
45503 if(this.fireEvent("deactivate", this) === false){
45508 this.fireEvent("activate", this);
45512 * Updates this panel's element (not for iframe)
45513 * @param {String} content The new content
45514 * @param {Boolean} loadScripts (optional) true to look for and process scripts
45516 setContent : function(content, loadScripts){
45521 this.el.update(content, loadScripts);
45524 ignoreResize : function(w, h)
45526 //return false; // always resize?
45527 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45530 this.lastSize = {width: w, height: h};
45535 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45536 * @return {Roo.UpdateManager} The UpdateManager
45538 getUpdateManager : function(){
45542 return this.el.getUpdateManager();
45545 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45546 * Does not work with IFRAME contents
45547 * @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:
45550 url: "your-url.php",
45551 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45552 callback: yourFunction,
45553 scope: yourObject, //(optional scope)
45556 text: "Loading...",
45562 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45563 * 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.
45564 * @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}
45565 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45566 * @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.
45567 * @return {Roo.ContentPanel} this
45575 var um = this.el.getUpdateManager();
45576 um.update.apply(um, arguments);
45582 * 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.
45583 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45584 * @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)
45585 * @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)
45586 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45588 setUrl : function(url, params, loadOnce){
45590 this.iframeEl.dom.src = url;
45594 if(this.refreshDelegate){
45595 this.removeListener("activate", this.refreshDelegate);
45597 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45598 this.on("activate", this.refreshDelegate);
45599 return this.el.getUpdateManager();
45602 _handleRefresh : function(url, params, loadOnce){
45603 if(!loadOnce || !this.loaded){
45604 var updater = this.el.getUpdateManager();
45605 updater.update(url, params, this._setLoaded.createDelegate(this));
45609 _setLoaded : function(){
45610 this.loaded = true;
45614 * Returns this panel's id
45617 getId : function(){
45622 * Returns this panel's element - used by regiosn to add.
45623 * @return {Roo.Element}
45625 getEl : function(){
45626 return this.wrapEl || this.el;
45631 adjustForComponents : function(width, height)
45633 //Roo.log('adjustForComponents ');
45634 if(this.resizeEl != this.el){
45635 width -= this.el.getFrameWidth('lr');
45636 height -= this.el.getFrameWidth('tb');
45639 var te = this.toolbar.getEl();
45640 te.setWidth(width);
45641 height -= te.getHeight();
45644 var te = this.footer.getEl();
45645 te.setWidth(width);
45646 height -= te.getHeight();
45650 if(this.adjustments){
45651 width += this.adjustments[0];
45652 height += this.adjustments[1];
45654 return {"width": width, "height": height};
45657 setSize : function(width, height){
45658 if(this.fitToFrame && !this.ignoreResize(width, height)){
45659 if(this.fitContainer && this.resizeEl != this.el){
45660 this.el.setSize(width, height);
45662 var size = this.adjustForComponents(width, height);
45664 this.iframeEl.setSize(width,height);
45667 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45668 this.fireEvent('resize', this, size.width, size.height);
45675 * Returns this panel's title
45678 getTitle : function(){
45680 if (typeof(this.title) != 'object') {
45685 for (var k in this.title) {
45686 if (!this.title.hasOwnProperty(k)) {
45690 if (k.indexOf('-') >= 0) {
45691 var s = k.split('-');
45692 for (var i = 0; i<s.length; i++) {
45693 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45696 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45703 * Set this panel's title
45704 * @param {String} title
45706 setTitle : function(title){
45707 this.title = title;
45709 this.region.updatePanelTitle(this, title);
45714 * Returns true is this panel was configured to be closable
45715 * @return {Boolean}
45717 isClosable : function(){
45718 return this.closable;
45721 beforeSlide : function(){
45723 this.resizeEl.clip();
45726 afterSlide : function(){
45728 this.resizeEl.unclip();
45732 * Force a content refresh from the URL specified in the {@link #setUrl} method.
45733 * Will fail silently if the {@link #setUrl} method has not been called.
45734 * This does not activate the panel, just updates its content.
45736 refresh : function(){
45737 if(this.refreshDelegate){
45738 this.loaded = false;
45739 this.refreshDelegate();
45744 * Destroys this panel
45746 destroy : function(){
45747 this.el.removeAllListeners();
45748 var tempEl = document.createElement("span");
45749 tempEl.appendChild(this.el.dom);
45750 tempEl.innerHTML = "";
45756 * form - if the content panel contains a form - this is a reference to it.
45757 * @type {Roo.form.Form}
45761 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45762 * This contains a reference to it.
45768 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45778 * @param {Object} cfg Xtype definition of item to add.
45782 getChildContainer: function () {
45783 return this.getEl();
45787 onScroll : function(e)
45789 this.fireEvent('scroll', this, e);
45794 var ret = new Roo.factory(cfg);
45799 if (cfg.xtype.match(/^Form$/)) {
45802 //if (this.footer) {
45803 // el = this.footer.container.insertSibling(false, 'before');
45805 el = this.el.createChild();
45808 this.form = new Roo.form.Form(cfg);
45811 if ( this.form.allItems.length) {
45812 this.form.render(el.dom);
45816 // should only have one of theses..
45817 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45818 // views.. should not be just added - used named prop 'view''
45820 cfg.el = this.el.appendChild(document.createElement("div"));
45823 var ret = new Roo.factory(cfg);
45825 ret.render && ret.render(false, ''); // render blank..
45835 * @class Roo.bootstrap.panel.Grid
45836 * @extends Roo.bootstrap.panel.Content
45838 * Create a new GridPanel.
45839 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45840 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45841 * @param {Object} config A the config object
45847 Roo.bootstrap.panel.Grid = function(config)
45851 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45852 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45854 config.el = this.wrapper;
45855 //this.el = this.wrapper;
45857 if (config.container) {
45858 // ctor'ed from a Border/panel.grid
45861 this.wrapper.setStyle("overflow", "hidden");
45862 this.wrapper.addClass('roo-grid-container');
45867 if(config.toolbar){
45868 var tool_el = this.wrapper.createChild();
45869 this.toolbar = Roo.factory(config.toolbar);
45871 if (config.toolbar.items) {
45872 ti = config.toolbar.items ;
45873 delete config.toolbar.items ;
45877 this.toolbar.render(tool_el);
45878 for(var i =0;i < ti.length;i++) {
45879 // Roo.log(['add child', items[i]]);
45880 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45882 this.toolbar.items = nitems;
45884 delete config.toolbar;
45887 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45888 config.grid.scrollBody = true;;
45889 config.grid.monitorWindowResize = false; // turn off autosizing
45890 config.grid.autoHeight = false;
45891 config.grid.autoWidth = false;
45893 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45895 if (config.background) {
45896 // render grid on panel activation (if panel background)
45897 this.on('activate', function(gp) {
45898 if (!gp.grid.rendered) {
45899 gp.grid.render(this.wrapper);
45900 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45905 this.grid.render(this.wrapper);
45906 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45909 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45910 // ??? needed ??? config.el = this.wrapper;
45915 // xtype created footer. - not sure if will work as we normally have to render first..
45916 if (this.footer && !this.footer.el && this.footer.xtype) {
45918 var ctr = this.grid.getView().getFooterPanel(true);
45919 this.footer.dataSource = this.grid.dataSource;
45920 this.footer = Roo.factory(this.footer, Roo);
45921 this.footer.render(ctr);
45931 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45934 getId : function(){
45935 return this.grid.id;
45939 * Returns the grid for this panel
45940 * @return {Roo.bootstrap.Table}
45942 getGrid : function(){
45946 setSize : function(width, height)
45949 //if(!this.ignoreResize(width, height)){
45950 var grid = this.grid;
45951 var size = this.adjustForComponents(width, height);
45952 // tfoot is not a footer?
45955 var gridel = grid.getGridEl();
45956 gridel.setSize(size.width, size.height);
45958 var tbd = grid.getGridEl().select('tbody', true).first();
45959 var thd = grid.getGridEl().select('thead',true).first();
45960 var tbf= grid.getGridEl().select('tfoot', true).first();
45963 size.height -= tbf.getHeight();
45966 size.height -= thd.getHeight();
45969 tbd.setSize(size.width, size.height );
45970 // this is for the account management tab -seems to work there.
45971 var thd = grid.getGridEl().select('thead',true).first();
45973 // tbd.setSize(size.width, size.height - thd.getHeight());
45983 beforeSlide : function(){
45984 this.grid.getView().scroller.clip();
45987 afterSlide : function(){
45988 this.grid.getView().scroller.unclip();
45991 destroy : function(){
45992 this.grid.destroy();
45994 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
45999 * @class Roo.bootstrap.panel.Nest
46000 * @extends Roo.bootstrap.panel.Content
46002 * Create a new Panel, that can contain a layout.Border.
46005 * @param {String/Object} config A string to set only the title or a config object
46007 Roo.bootstrap.panel.Nest = function(config)
46009 // construct with only one argument..
46010 /* FIXME - implement nicer consturctors
46011 if (layout.layout) {
46013 layout = config.layout;
46014 delete config.layout;
46016 if (layout.xtype && !layout.getEl) {
46017 // then layout needs constructing..
46018 layout = Roo.factory(layout, Roo);
46022 config.el = config.layout.getEl();
46024 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46026 config.layout.monitorWindowResize = false; // turn off autosizing
46027 this.layout = config.layout;
46028 this.layout.getEl().addClass("roo-layout-nested-layout");
46029 this.layout.parent = this;
46036 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46038 * @cfg {Roo.BorderLayout} layout The layout for this panel
46042 setSize : function(width, height){
46043 if(!this.ignoreResize(width, height)){
46044 var size = this.adjustForComponents(width, height);
46045 var el = this.layout.getEl();
46046 if (size.height < 1) {
46047 el.setWidth(size.width);
46049 el.setSize(size.width, size.height);
46051 var touch = el.dom.offsetWidth;
46052 this.layout.layout();
46053 // ie requires a double layout on the first pass
46054 if(Roo.isIE && !this.initialized){
46055 this.initialized = true;
46056 this.layout.layout();
46061 // activate all subpanels if not currently active..
46063 setActiveState : function(active){
46064 this.active = active;
46065 this.setActiveClass(active);
46068 this.fireEvent("deactivate", this);
46072 this.fireEvent("activate", this);
46073 // not sure if this should happen before or after..
46074 if (!this.layout) {
46075 return; // should not happen..
46078 for (var r in this.layout.regions) {
46079 reg = this.layout.getRegion(r);
46080 if (reg.getActivePanel()) {
46081 //reg.showPanel(reg.getActivePanel()); // force it to activate..
46082 reg.setActivePanel(reg.getActivePanel());
46085 if (!reg.panels.length) {
46088 reg.showPanel(reg.getPanel(0));
46097 * Returns the nested BorderLayout for this panel
46098 * @return {Roo.BorderLayout}
46100 getLayout : function(){
46101 return this.layout;
46105 * Adds a xtype elements to the layout of the nested panel
46109 xtype : 'ContentPanel',
46116 xtype : 'NestedLayoutPanel',
46122 items : [ ... list of content panels or nested layout panels.. ]
46126 * @param {Object} cfg Xtype definition of item to add.
46128 addxtype : function(cfg) {
46129 return this.layout.addxtype(cfg);
46134 * Ext JS Library 1.1.1
46135 * Copyright(c) 2006-2007, Ext JS, LLC.
46137 * Originally Released Under LGPL - original licence link has changed is not relivant.
46140 * <script type="text/javascript">
46143 * @class Roo.TabPanel
46144 * @extends Roo.util.Observable
46145 * A lightweight tab container.
46149 // basic tabs 1, built from existing content
46150 var tabs = new Roo.TabPanel("tabs1");
46151 tabs.addTab("script", "View Script");
46152 tabs.addTab("markup", "View Markup");
46153 tabs.activate("script");
46155 // more advanced tabs, built from javascript
46156 var jtabs = new Roo.TabPanel("jtabs");
46157 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46159 // set up the UpdateManager
46160 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46161 var updater = tab2.getUpdateManager();
46162 updater.setDefaultUrl("ajax1.htm");
46163 tab2.on('activate', updater.refresh, updater, true);
46165 // Use setUrl for Ajax loading
46166 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46167 tab3.setUrl("ajax2.htm", null, true);
46170 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46173 jtabs.activate("jtabs-1");
46176 * Create a new TabPanel.
46177 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46178 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46180 Roo.bootstrap.panel.Tabs = function(config){
46182 * The container element for this TabPanel.
46183 * @type Roo.Element
46185 this.el = Roo.get(config.el);
46188 if(typeof config == "boolean"){
46189 this.tabPosition = config ? "bottom" : "top";
46191 Roo.apply(this, config);
46195 if(this.tabPosition == "bottom"){
46196 // if tabs are at the bottom = create the body first.
46197 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46198 this.el.addClass("roo-tabs-bottom");
46200 // next create the tabs holders
46202 if (this.tabPosition == "west"){
46204 var reg = this.region; // fake it..
46206 if (!reg.mgr.parent) {
46209 reg = reg.mgr.parent.region;
46211 Roo.log("got nest?");
46213 if (reg.mgr.getRegion('west')) {
46214 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46215 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46216 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46217 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46218 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46226 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46227 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46228 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46229 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46234 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46237 // finally - if tabs are at the top, then create the body last..
46238 if(this.tabPosition != "bottom"){
46239 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46240 * @type Roo.Element
46242 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46243 this.el.addClass("roo-tabs-top");
46247 this.bodyEl.setStyle("position", "relative");
46249 this.active = null;
46250 this.activateDelegate = this.activate.createDelegate(this);
46255 * Fires when the active tab changes
46256 * @param {Roo.TabPanel} this
46257 * @param {Roo.TabPanelItem} activePanel The new active tab
46261 * @event beforetabchange
46262 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46263 * @param {Roo.TabPanel} this
46264 * @param {Object} e Set cancel to true on this object to cancel the tab change
46265 * @param {Roo.TabPanelItem} tab The tab being changed to
46267 "beforetabchange" : true
46270 Roo.EventManager.onWindowResize(this.onResize, this);
46271 this.cpad = this.el.getPadding("lr");
46272 this.hiddenCount = 0;
46275 // toolbar on the tabbar support...
46276 if (this.toolbar) {
46277 alert("no toolbar support yet");
46278 this.toolbar = false;
46280 var tcfg = this.toolbar;
46281 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
46282 this.toolbar = new Roo.Toolbar(tcfg);
46283 if (Roo.isSafari) {
46284 var tbl = tcfg.container.child('table', true);
46285 tbl.setAttribute('width', '100%');
46293 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46296 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46298 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46300 tabPosition : "top",
46302 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46304 currentTabWidth : 0,
46306 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46310 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46314 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46316 preferredTabWidth : 175,
46318 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46320 resizeTabs : false,
46322 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46324 monitorResize : true,
46326 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
46328 toolbar : false, // set by caller..
46330 region : false, /// set by caller
46332 disableTooltips : true, // not used yet...
46335 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46336 * @param {String} id The id of the div to use <b>or create</b>
46337 * @param {String} text The text for the tab
46338 * @param {String} content (optional) Content to put in the TabPanelItem body
46339 * @param {Boolean} closable (optional) True to create a close icon on the tab
46340 * @return {Roo.TabPanelItem} The created TabPanelItem
46342 addTab : function(id, text, content, closable, tpl)
46344 var item = new Roo.bootstrap.panel.TabItem({
46348 closable : closable,
46351 this.addTabItem(item);
46353 item.setContent(content);
46359 * Returns the {@link Roo.TabPanelItem} with the specified id/index
46360 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46361 * @return {Roo.TabPanelItem}
46363 getTab : function(id){
46364 return this.items[id];
46368 * Hides the {@link Roo.TabPanelItem} with the specified id/index
46369 * @param {String/Number} id The id or index of the TabPanelItem to hide.
46371 hideTab : function(id){
46372 var t = this.items[id];
46375 this.hiddenCount++;
46376 this.autoSizeTabs();
46381 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46382 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46384 unhideTab : function(id){
46385 var t = this.items[id];
46387 t.setHidden(false);
46388 this.hiddenCount--;
46389 this.autoSizeTabs();
46394 * Adds an existing {@link Roo.TabPanelItem}.
46395 * @param {Roo.TabPanelItem} item The TabPanelItem to add
46397 addTabItem : function(item)
46399 this.items[item.id] = item;
46400 this.items.push(item);
46401 this.autoSizeTabs();
46402 // if(this.resizeTabs){
46403 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46404 // this.autoSizeTabs();
46406 // item.autoSize();
46411 * Removes a {@link Roo.TabPanelItem}.
46412 * @param {String/Number} id The id or index of the TabPanelItem to remove.
46414 removeTab : function(id){
46415 var items = this.items;
46416 var tab = items[id];
46417 if(!tab) { return; }
46418 var index = items.indexOf(tab);
46419 if(this.active == tab && items.length > 1){
46420 var newTab = this.getNextAvailable(index);
46425 this.stripEl.dom.removeChild(tab.pnode.dom);
46426 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46427 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46429 items.splice(index, 1);
46430 delete this.items[tab.id];
46431 tab.fireEvent("close", tab);
46432 tab.purgeListeners();
46433 this.autoSizeTabs();
46436 getNextAvailable : function(start){
46437 var items = this.items;
46439 // look for a next tab that will slide over to
46440 // replace the one being removed
46441 while(index < items.length){
46442 var item = items[++index];
46443 if(item && !item.isHidden()){
46447 // if one isn't found select the previous tab (on the left)
46450 var item = items[--index];
46451 if(item && !item.isHidden()){
46459 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46460 * @param {String/Number} id The id or index of the TabPanelItem to disable.
46462 disableTab : function(id){
46463 var tab = this.items[id];
46464 if(tab && this.active != tab){
46470 * Enables a {@link Roo.TabPanelItem} that is disabled.
46471 * @param {String/Number} id The id or index of the TabPanelItem to enable.
46473 enableTab : function(id){
46474 var tab = this.items[id];
46479 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46480 * @param {String/Number} id The id or index of the TabPanelItem to activate.
46481 * @return {Roo.TabPanelItem} The TabPanelItem.
46483 activate : function(id)
46485 //Roo.log('activite:' + id);
46487 var tab = this.items[id];
46491 if(tab == this.active || tab.disabled){
46495 this.fireEvent("beforetabchange", this, e, tab);
46496 if(e.cancel !== true && !tab.disabled){
46498 this.active.hide();
46500 this.active = this.items[id];
46501 this.active.show();
46502 this.fireEvent("tabchange", this, this.active);
46508 * Gets the active {@link Roo.TabPanelItem}.
46509 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46511 getActiveTab : function(){
46512 return this.active;
46516 * Updates the tab body element to fit the height of the container element
46517 * for overflow scrolling
46518 * @param {Number} targetHeight (optional) Override the starting height from the elements height
46520 syncHeight : function(targetHeight){
46521 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46522 var bm = this.bodyEl.getMargins();
46523 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46524 this.bodyEl.setHeight(newHeight);
46528 onResize : function(){
46529 if(this.monitorResize){
46530 this.autoSizeTabs();
46535 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46537 beginUpdate : function(){
46538 this.updating = true;
46542 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46544 endUpdate : function(){
46545 this.updating = false;
46546 this.autoSizeTabs();
46550 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46552 autoSizeTabs : function()
46554 var count = this.items.length;
46555 var vcount = count - this.hiddenCount;
46558 this.stripEl.hide();
46560 this.stripEl.show();
46563 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46568 var w = Math.max(this.el.getWidth() - this.cpad, 10);
46569 var availWidth = Math.floor(w / vcount);
46570 var b = this.stripBody;
46571 if(b.getWidth() > w){
46572 var tabs = this.items;
46573 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46574 if(availWidth < this.minTabWidth){
46575 /*if(!this.sleft){ // incomplete scrolling code
46576 this.createScrollButtons();
46579 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46582 if(this.currentTabWidth < this.preferredTabWidth){
46583 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46589 * Returns the number of tabs in this TabPanel.
46592 getCount : function(){
46593 return this.items.length;
46597 * Resizes all the tabs to the passed width
46598 * @param {Number} The new width
46600 setTabWidth : function(width){
46601 this.currentTabWidth = width;
46602 for(var i = 0, len = this.items.length; i < len; i++) {
46603 if(!this.items[i].isHidden()) {
46604 this.items[i].setWidth(width);
46610 * Destroys this TabPanel
46611 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46613 destroy : function(removeEl){
46614 Roo.EventManager.removeResizeListener(this.onResize, this);
46615 for(var i = 0, len = this.items.length; i < len; i++){
46616 this.items[i].purgeListeners();
46618 if(removeEl === true){
46619 this.el.update("");
46624 createStrip : function(container)
46626 var strip = document.createElement("nav");
46627 strip.className = Roo.bootstrap.version == 4 ?
46628 "navbar-light bg-light" :
46629 "navbar navbar-default"; //"x-tabs-wrap";
46630 container.appendChild(strip);
46634 createStripList : function(strip)
46636 // div wrapper for retard IE
46637 // returns the "tr" element.
46638 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46639 //'<div class="x-tabs-strip-wrap">'+
46640 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46641 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46642 return strip.firstChild; //.firstChild.firstChild.firstChild;
46644 createBody : function(container)
46646 var body = document.createElement("div");
46647 Roo.id(body, "tab-body");
46648 //Roo.fly(body).addClass("x-tabs-body");
46649 Roo.fly(body).addClass("tab-content");
46650 container.appendChild(body);
46653 createItemBody :function(bodyEl, id){
46654 var body = Roo.getDom(id);
46656 body = document.createElement("div");
46659 //Roo.fly(body).addClass("x-tabs-item-body");
46660 Roo.fly(body).addClass("tab-pane");
46661 bodyEl.insertBefore(body, bodyEl.firstChild);
46665 createStripElements : function(stripEl, text, closable, tpl)
46667 var td = document.createElement("li"); // was td..
46668 td.className = 'nav-item';
46670 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46673 stripEl.appendChild(td);
46675 td.className = "x-tabs-closable";
46676 if(!this.closeTpl){
46677 this.closeTpl = new Roo.Template(
46678 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46679 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46680 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
46683 var el = this.closeTpl.overwrite(td, {"text": text});
46684 var close = el.getElementsByTagName("div")[0];
46685 var inner = el.getElementsByTagName("em")[0];
46686 return {"el": el, "close": close, "inner": inner};
46689 // not sure what this is..
46690 // if(!this.tabTpl){
46691 //this.tabTpl = new Roo.Template(
46692 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46693 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46695 // this.tabTpl = new Roo.Template(
46696 // '<a href="#">' +
46697 // '<span unselectable="on"' +
46698 // (this.disableTooltips ? '' : ' title="{text}"') +
46699 // ' >{text}</span></a>'
46705 var template = tpl || this.tabTpl || false;
46708 template = new Roo.Template(
46709 Roo.bootstrap.version == 4 ?
46711 '<a class="nav-link" href="#" unselectable="on"' +
46712 (this.disableTooltips ? '' : ' title="{text}"') +
46715 '<a class="nav-link" href="#">' +
46716 '<span unselectable="on"' +
46717 (this.disableTooltips ? '' : ' title="{text}"') +
46718 ' >{text}</span></a>'
46723 switch (typeof(template)) {
46727 template = new Roo.Template(template);
46733 var el = template.overwrite(td, {"text": text});
46735 var inner = el.getElementsByTagName("span")[0];
46737 return {"el": el, "inner": inner};
46745 * @class Roo.TabPanelItem
46746 * @extends Roo.util.Observable
46747 * Represents an individual item (tab plus body) in a TabPanel.
46748 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46749 * @param {String} id The id of this TabPanelItem
46750 * @param {String} text The text for the tab of this TabPanelItem
46751 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46753 Roo.bootstrap.panel.TabItem = function(config){
46755 * The {@link Roo.TabPanel} this TabPanelItem belongs to
46756 * @type Roo.TabPanel
46758 this.tabPanel = config.panel;
46760 * The id for this TabPanelItem
46763 this.id = config.id;
46765 this.disabled = false;
46767 this.text = config.text;
46769 this.loaded = false;
46770 this.closable = config.closable;
46773 * The body element for this TabPanelItem.
46774 * @type Roo.Element
46776 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46777 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46778 this.bodyEl.setStyle("display", "block");
46779 this.bodyEl.setStyle("zoom", "1");
46780 //this.hideAction();
46782 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46784 this.el = Roo.get(els.el);
46785 this.inner = Roo.get(els.inner, true);
46786 this.textEl = Roo.bootstrap.version == 4 ?
46787 this.el : Roo.get(this.el.dom.firstChild, true);
46789 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46790 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46793 // this.el.on("mousedown", this.onTabMouseDown, this);
46794 this.el.on("click", this.onTabClick, this);
46796 if(config.closable){
46797 var c = Roo.get(els.close, true);
46798 c.dom.title = this.closeText;
46799 c.addClassOnOver("close-over");
46800 c.on("click", this.closeClick, this);
46806 * Fires when this tab becomes the active tab.
46807 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46808 * @param {Roo.TabPanelItem} this
46812 * @event beforeclose
46813 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46814 * @param {Roo.TabPanelItem} this
46815 * @param {Object} e Set cancel to true on this object to cancel the close.
46817 "beforeclose": true,
46820 * Fires when this tab is closed.
46821 * @param {Roo.TabPanelItem} this
46825 * @event deactivate
46826 * Fires when this tab is no longer the active tab.
46827 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46828 * @param {Roo.TabPanelItem} this
46830 "deactivate" : true
46832 this.hidden = false;
46834 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46837 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46839 purgeListeners : function(){
46840 Roo.util.Observable.prototype.purgeListeners.call(this);
46841 this.el.removeAllListeners();
46844 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46847 this.status_node.addClass("active");
46850 this.tabPanel.stripWrap.repaint();
46852 this.fireEvent("activate", this.tabPanel, this);
46856 * Returns true if this tab is the active tab.
46857 * @return {Boolean}
46859 isActive : function(){
46860 return this.tabPanel.getActiveTab() == this;
46864 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46867 this.status_node.removeClass("active");
46869 this.fireEvent("deactivate", this.tabPanel, this);
46872 hideAction : function(){
46873 this.bodyEl.hide();
46874 this.bodyEl.setStyle("position", "absolute");
46875 this.bodyEl.setLeft("-20000px");
46876 this.bodyEl.setTop("-20000px");
46879 showAction : function(){
46880 this.bodyEl.setStyle("position", "relative");
46881 this.bodyEl.setTop("");
46882 this.bodyEl.setLeft("");
46883 this.bodyEl.show();
46887 * Set the tooltip for the tab.
46888 * @param {String} tooltip The tab's tooltip
46890 setTooltip : function(text){
46891 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46892 this.textEl.dom.qtip = text;
46893 this.textEl.dom.removeAttribute('title');
46895 this.textEl.dom.title = text;
46899 onTabClick : function(e){
46900 e.preventDefault();
46901 this.tabPanel.activate(this.id);
46904 onTabMouseDown : function(e){
46905 e.preventDefault();
46906 this.tabPanel.activate(this.id);
46909 getWidth : function(){
46910 return this.inner.getWidth();
46913 setWidth : function(width){
46914 var iwidth = width - this.linode.getPadding("lr");
46915 this.inner.setWidth(iwidth);
46916 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46917 this.linode.setWidth(width);
46921 * Show or hide the tab
46922 * @param {Boolean} hidden True to hide or false to show.
46924 setHidden : function(hidden){
46925 this.hidden = hidden;
46926 this.linode.setStyle("display", hidden ? "none" : "");
46930 * Returns true if this tab is "hidden"
46931 * @return {Boolean}
46933 isHidden : function(){
46934 return this.hidden;
46938 * Returns the text for this tab
46941 getText : function(){
46945 autoSize : function(){
46946 //this.el.beginMeasure();
46947 this.textEl.setWidth(1);
46949 * #2804 [new] Tabs in Roojs
46950 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46952 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46953 //this.el.endMeasure();
46957 * Sets the text for the tab (Note: this also sets the tooltip text)
46958 * @param {String} text The tab's text and tooltip
46960 setText : function(text){
46962 this.textEl.update(text);
46963 this.setTooltip(text);
46964 //if(!this.tabPanel.resizeTabs){
46965 // this.autoSize();
46969 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46971 activate : function(){
46972 this.tabPanel.activate(this.id);
46976 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46978 disable : function(){
46979 if(this.tabPanel.active != this){
46980 this.disabled = true;
46981 this.status_node.addClass("disabled");
46986 * Enables this TabPanelItem if it was previously disabled.
46988 enable : function(){
46989 this.disabled = false;
46990 this.status_node.removeClass("disabled");
46994 * Sets the content for this TabPanelItem.
46995 * @param {String} content The content
46996 * @param {Boolean} loadScripts true to look for and load scripts
46998 setContent : function(content, loadScripts){
46999 this.bodyEl.update(content, loadScripts);
47003 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47004 * @return {Roo.UpdateManager} The UpdateManager
47006 getUpdateManager : function(){
47007 return this.bodyEl.getUpdateManager();
47011 * Set a URL to be used to load the content for this TabPanelItem.
47012 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47013 * @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)
47014 * @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)
47015 * @return {Roo.UpdateManager} The UpdateManager
47017 setUrl : function(url, params, loadOnce){
47018 if(this.refreshDelegate){
47019 this.un('activate', this.refreshDelegate);
47021 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47022 this.on("activate", this.refreshDelegate);
47023 return this.bodyEl.getUpdateManager();
47027 _handleRefresh : function(url, params, loadOnce){
47028 if(!loadOnce || !this.loaded){
47029 var updater = this.bodyEl.getUpdateManager();
47030 updater.update(url, params, this._setLoaded.createDelegate(this));
47035 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
47036 * Will fail silently if the setUrl method has not been called.
47037 * This does not activate the panel, just updates its content.
47039 refresh : function(){
47040 if(this.refreshDelegate){
47041 this.loaded = false;
47042 this.refreshDelegate();
47047 _setLoaded : function(){
47048 this.loaded = true;
47052 closeClick : function(e){
47055 this.fireEvent("beforeclose", this, o);
47056 if(o.cancel !== true){
47057 this.tabPanel.removeTab(this.id);
47061 * The text displayed in the tooltip for the close icon.
47064 closeText : "Close this tab"
47067 * This script refer to:
47068 * Title: International Telephone Input
47069 * Author: Jack O'Connor
47070 * Code version: v12.1.12
47071 * Availability: https://github.com/jackocnr/intl-tel-input.git
47074 Roo.bootstrap.form.PhoneInputData = function() {
47077 "Afghanistan (افغانستان)",
47082 "Albania (Shqipëri)",
47087 "Algeria (الجزائر)",
47112 "Antigua and Barbuda",
47122 "Armenia (Հայաստան)",
47138 "Austria (Österreich)",
47143 "Azerbaijan (Azərbaycan)",
47153 "Bahrain (البحرين)",
47158 "Bangladesh (বাংলাদেশ)",
47168 "Belarus (Беларусь)",
47173 "Belgium (België)",
47203 "Bosnia and Herzegovina (Босна и Херцеговина)",
47218 "British Indian Ocean Territory",
47223 "British Virgin Islands",
47233 "Bulgaria (България)",
47243 "Burundi (Uburundi)",
47248 "Cambodia (កម្ពុជា)",
47253 "Cameroon (Cameroun)",
47262 ["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"]
47265 "Cape Verde (Kabu Verdi)",
47270 "Caribbean Netherlands",
47281 "Central African Republic (République centrafricaine)",
47301 "Christmas Island",
47307 "Cocos (Keeling) Islands",
47318 "Comoros (جزر القمر)",
47323 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47328 "Congo (Republic) (Congo-Brazzaville)",
47348 "Croatia (Hrvatska)",
47369 "Czech Republic (Česká republika)",
47374 "Denmark (Danmark)",
47389 "Dominican Republic (República Dominicana)",
47393 ["809", "829", "849"]
47411 "Equatorial Guinea (Guinea Ecuatorial)",
47431 "Falkland Islands (Islas Malvinas)",
47436 "Faroe Islands (Føroyar)",
47457 "French Guiana (Guyane française)",
47462 "French Polynesia (Polynésie française)",
47477 "Georgia (საქართველო)",
47482 "Germany (Deutschland)",
47502 "Greenland (Kalaallit Nunaat)",
47539 "Guinea-Bissau (Guiné Bissau)",
47564 "Hungary (Magyarország)",
47569 "Iceland (Ísland)",
47589 "Iraq (العراق)",
47605 "Israel (ישראל)",
47632 "Jordan (الأردن)",
47637 "Kazakhstan (Казахстан)",
47658 "Kuwait (الكويت)",
47663 "Kyrgyzstan (Кыргызстан)",
47673 "Latvia (Latvija)",
47678 "Lebanon (لبنان)",
47693 "Libya (ليبيا)",
47703 "Lithuania (Lietuva)",
47718 "Macedonia (FYROM) (Македонија)",
47723 "Madagascar (Madagasikara)",
47753 "Marshall Islands",
47763 "Mauritania (موريتانيا)",
47768 "Mauritius (Moris)",
47789 "Moldova (Republica Moldova)",
47799 "Mongolia (Монгол)",
47804 "Montenegro (Crna Gora)",
47814 "Morocco (المغرب)",
47820 "Mozambique (Moçambique)",
47825 "Myanmar (Burma) (မြန်မာ)",
47830 "Namibia (Namibië)",
47845 "Netherlands (Nederland)",
47850 "New Caledonia (Nouvelle-Calédonie)",
47885 "North Korea (조선 민주주의 인민 공화국)",
47890 "Northern Mariana Islands",
47906 "Pakistan (پاکستان)",
47916 "Palestine (فلسطين)",
47926 "Papua New Guinea",
47968 "Réunion (La Réunion)",
47974 "Romania (România)",
47990 "Saint Barthélemy",
48001 "Saint Kitts and Nevis",
48011 "Saint Martin (Saint-Martin (partie française))",
48017 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48022 "Saint Vincent and the Grenadines",
48037 "São Tomé and Príncipe (São Tomé e Príncipe)",
48042 "Saudi Arabia (المملكة العربية السعودية)",
48047 "Senegal (Sénégal)",
48077 "Slovakia (Slovensko)",
48082 "Slovenia (Slovenija)",
48092 "Somalia (Soomaaliya)",
48102 "South Korea (대한민국)",
48107 "South Sudan (جنوب السودان)",
48117 "Sri Lanka (ශ්රී ලංකාව)",
48122 "Sudan (السودان)",
48132 "Svalbard and Jan Mayen",
48143 "Sweden (Sverige)",
48148 "Switzerland (Schweiz)",
48153 "Syria (سوريا)",
48198 "Trinidad and Tobago",
48203 "Tunisia (تونس)",
48208 "Turkey (Türkiye)",
48218 "Turks and Caicos Islands",
48228 "U.S. Virgin Islands",
48238 "Ukraine (Україна)",
48243 "United Arab Emirates (الإمارات العربية المتحدة)",
48265 "Uzbekistan (Oʻzbekiston)",
48275 "Vatican City (Città del Vaticano)",
48286 "Vietnam (Việt Nam)",
48291 "Wallis and Futuna (Wallis-et-Futuna)",
48296 "Western Sahara (الصحراء الغربية)",
48302 "Yemen (اليمن)",
48326 * This script refer to:
48327 * Title: International Telephone Input
48328 * Author: Jack O'Connor
48329 * Code version: v12.1.12
48330 * Availability: https://github.com/jackocnr/intl-tel-input.git
48334 * @class Roo.bootstrap.form.PhoneInput
48335 * @extends Roo.bootstrap.form.TriggerField
48336 * An input with International dial-code selection
48338 * @cfg {String} defaultDialCode default '+852'
48339 * @cfg {Array} preferedCountries default []
48342 * Create a new PhoneInput.
48343 * @param {Object} config Configuration options
48346 Roo.bootstrap.form.PhoneInput = function(config) {
48347 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48350 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48352 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48354 listWidth: undefined,
48356 selectedClass: 'active',
48358 invalidClass : "has-warning",
48360 validClass: 'has-success',
48362 allowed: '0123456789',
48367 * @cfg {String} defaultDialCode The default dial code when initializing the input
48369 defaultDialCode: '+852',
48372 * @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
48374 preferedCountries: false,
48376 getAutoCreate : function()
48378 var data = Roo.bootstrap.form.PhoneInputData();
48379 var align = this.labelAlign || this.parentLabelAlign();
48382 this.allCountries = [];
48383 this.dialCodeMapping = [];
48385 for (var i = 0; i < data.length; i++) {
48387 this.allCountries[i] = {
48391 priority: c[3] || 0,
48392 areaCodes: c[4] || null
48394 this.dialCodeMapping[c[2]] = {
48397 priority: c[3] || 0,
48398 areaCodes: c[4] || null
48410 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48411 maxlength: this.max_length,
48412 cls : 'form-control tel-input',
48413 autocomplete: 'new-password'
48416 var hiddenInput = {
48419 cls: 'hidden-tel-input'
48423 hiddenInput.name = this.name;
48426 if (this.disabled) {
48427 input.disabled = true;
48430 var flag_container = {
48447 cls: this.hasFeedback ? 'has-feedback' : '',
48453 cls: 'dial-code-holder',
48460 cls: 'roo-select2-container input-group',
48467 if (this.fieldLabel.length) {
48470 tooltip: 'This field is required'
48476 cls: 'control-label',
48482 html: this.fieldLabel
48485 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48491 if(this.indicatorpos == 'right') {
48492 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48499 if(align == 'left') {
48507 if(this.labelWidth > 12){
48508 label.style = "width: " + this.labelWidth + 'px';
48510 if(this.labelWidth < 13 && this.labelmd == 0){
48511 this.labelmd = this.labelWidth;
48513 if(this.labellg > 0){
48514 label.cls += ' col-lg-' + this.labellg;
48515 input.cls += ' col-lg-' + (12 - this.labellg);
48517 if(this.labelmd > 0){
48518 label.cls += ' col-md-' + this.labelmd;
48519 container.cls += ' col-md-' + (12 - this.labelmd);
48521 if(this.labelsm > 0){
48522 label.cls += ' col-sm-' + this.labelsm;
48523 container.cls += ' col-sm-' + (12 - this.labelsm);
48525 if(this.labelxs > 0){
48526 label.cls += ' col-xs-' + this.labelxs;
48527 container.cls += ' col-xs-' + (12 - this.labelxs);
48537 var settings = this;
48539 ['xs','sm','md','lg'].map(function(size){
48540 if (settings[size]) {
48541 cfg.cls += ' col-' + size + '-' + settings[size];
48545 this.store = new Roo.data.Store({
48546 proxy : new Roo.data.MemoryProxy({}),
48547 reader : new Roo.data.JsonReader({
48558 'name' : 'dialCode',
48562 'name' : 'priority',
48566 'name' : 'areaCodes',
48573 if(!this.preferedCountries) {
48574 this.preferedCountries = [
48581 var p = this.preferedCountries.reverse();
48584 for (var i = 0; i < p.length; i++) {
48585 for (var j = 0; j < this.allCountries.length; j++) {
48586 if(this.allCountries[j].iso2 == p[i]) {
48587 var t = this.allCountries[j];
48588 this.allCountries.splice(j,1);
48589 this.allCountries.unshift(t);
48595 this.store.proxy.data = {
48597 data: this.allCountries
48603 initEvents : function()
48606 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48608 this.indicator = this.indicatorEl();
48609 this.flag = this.flagEl();
48610 this.dialCodeHolder = this.dialCodeHolderEl();
48612 this.trigger = this.el.select('div.flag-box',true).first();
48613 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48618 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48619 _this.list.setWidth(lw);
48622 this.list.on('mouseover', this.onViewOver, this);
48623 this.list.on('mousemove', this.onViewMove, this);
48624 this.inputEl().on("keyup", this.onKeyUp, this);
48625 this.inputEl().on("keypress", this.onKeyPress, this);
48627 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48629 this.view = new Roo.View(this.list, this.tpl, {
48630 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48633 this.view.on('click', this.onViewClick, this);
48634 this.setValue(this.defaultDialCode);
48637 onTriggerClick : function(e)
48639 Roo.log('trigger click');
48644 if(this.isExpanded()){
48646 this.hasFocus = false;
48648 this.store.load({});
48649 this.hasFocus = true;
48654 isExpanded : function()
48656 return this.list.isVisible();
48659 collapse : function()
48661 if(!this.isExpanded()){
48665 Roo.get(document).un('mousedown', this.collapseIf, this);
48666 Roo.get(document).un('mousewheel', this.collapseIf, this);
48667 this.fireEvent('collapse', this);
48671 expand : function()
48675 if(this.isExpanded() || !this.hasFocus){
48679 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48680 this.list.setWidth(lw);
48683 this.restrictHeight();
48685 Roo.get(document).on('mousedown', this.collapseIf, this);
48686 Roo.get(document).on('mousewheel', this.collapseIf, this);
48688 this.fireEvent('expand', this);
48691 restrictHeight : function()
48693 this.list.alignTo(this.inputEl(), this.listAlign);
48694 this.list.alignTo(this.inputEl(), this.listAlign);
48697 onViewOver : function(e, t)
48699 if(this.inKeyMode){
48702 var item = this.view.findItemFromChild(t);
48705 var index = this.view.indexOf(item);
48706 this.select(index, false);
48711 onViewClick : function(view, doFocus, el, e)
48713 var index = this.view.getSelectedIndexes()[0];
48715 var r = this.store.getAt(index);
48718 this.onSelect(r, index);
48720 if(doFocus !== false && !this.blockFocus){
48721 this.inputEl().focus();
48725 onViewMove : function(e, t)
48727 this.inKeyMode = false;
48730 select : function(index, scrollIntoView)
48732 this.selectedIndex = index;
48733 this.view.select(index);
48734 if(scrollIntoView !== false){
48735 var el = this.view.getNode(index);
48737 this.list.scrollChildIntoView(el, false);
48742 createList : function()
48744 this.list = Roo.get(document.body).createChild({
48746 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48747 style: 'display:none'
48750 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48753 collapseIf : function(e)
48755 var in_combo = e.within(this.el);
48756 var in_list = e.within(this.list);
48757 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48759 if (in_combo || in_list || is_list) {
48765 onSelect : function(record, index)
48767 if(this.fireEvent('beforeselect', this, record, index) !== false){
48769 this.setFlagClass(record.data.iso2);
48770 this.setDialCode(record.data.dialCode);
48771 this.hasFocus = false;
48773 this.fireEvent('select', this, record, index);
48777 flagEl : function()
48779 var flag = this.el.select('div.flag',true).first();
48786 dialCodeHolderEl : function()
48788 var d = this.el.select('input.dial-code-holder',true).first();
48795 setDialCode : function(v)
48797 this.dialCodeHolder.dom.value = '+'+v;
48800 setFlagClass : function(n)
48802 this.flag.dom.className = 'flag '+n;
48805 getValue : function()
48807 var v = this.inputEl().getValue();
48808 if(this.dialCodeHolder) {
48809 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48814 setValue : function(v)
48816 var d = this.getDialCode(v);
48818 //invalid dial code
48819 if(v.length == 0 || !d || d.length == 0) {
48821 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48822 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48828 this.setFlagClass(this.dialCodeMapping[d].iso2);
48829 this.setDialCode(d);
48830 this.inputEl().dom.value = v.replace('+'+d,'');
48831 this.hiddenEl().dom.value = this.getValue();
48836 getDialCode : function(v)
48840 if (v.length == 0) {
48841 return this.dialCodeHolder.dom.value;
48845 if (v.charAt(0) != "+") {
48848 var numericChars = "";
48849 for (var i = 1; i < v.length; i++) {
48850 var c = v.charAt(i);
48853 if (this.dialCodeMapping[numericChars]) {
48854 dialCode = v.substr(1, i);
48856 if (numericChars.length == 4) {
48866 this.setValue(this.defaultDialCode);
48870 hiddenEl : function()
48872 return this.el.select('input.hidden-tel-input',true).first();
48875 // after setting val
48876 onKeyUp : function(e){
48877 this.setValue(this.getValue());
48880 onKeyPress : function(e){
48881 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48888 * @class Roo.bootstrap.form.MoneyField
48889 * @extends Roo.bootstrap.form.ComboBox
48890 * Bootstrap MoneyField class
48893 * Create a new MoneyField.
48894 * @param {Object} config Configuration options
48897 Roo.bootstrap.form.MoneyField = function(config) {
48899 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48903 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48906 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48908 allowDecimals : true,
48910 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48912 decimalSeparator : ".",
48914 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48916 decimalPrecision : 0,
48918 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48920 allowNegative : true,
48922 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48926 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48928 minValue : Number.NEGATIVE_INFINITY,
48930 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48932 maxValue : Number.MAX_VALUE,
48934 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48936 minText : "The minimum value for this field is {0}",
48938 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48940 maxText : "The maximum value for this field is {0}",
48942 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48943 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48945 nanText : "{0} is not a valid number",
48947 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48951 * @cfg {String} defaults currency of the MoneyField
48952 * value should be in lkey
48954 defaultCurrency : false,
48956 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48958 thousandsDelimiter : false,
48960 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48969 * @cfg {Roo.data.Store} store Store to lookup currency??
48973 getAutoCreate : function()
48975 var align = this.labelAlign || this.parentLabelAlign();
48987 cls : 'form-control roo-money-amount-input',
48988 autocomplete: 'new-password'
48991 var hiddenInput = {
48995 cls: 'hidden-number-input'
48998 if(this.max_length) {
48999 input.maxlength = this.max_length;
49003 hiddenInput.name = this.name;
49006 if (this.disabled) {
49007 input.disabled = true;
49010 var clg = 12 - this.inputlg;
49011 var cmd = 12 - this.inputmd;
49012 var csm = 12 - this.inputsm;
49013 var cxs = 12 - this.inputxs;
49017 cls : 'row roo-money-field',
49021 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49025 cls: 'roo-select2-container input-group',
49029 cls : 'form-control roo-money-currency-input',
49030 autocomplete: 'new-password',
49032 name : this.currencyName
49036 cls : 'input-group-addon',
49050 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49054 cls: this.hasFeedback ? 'has-feedback' : '',
49065 if (this.fieldLabel.length) {
49068 tooltip: 'This field is required'
49074 cls: 'control-label',
49080 html: this.fieldLabel
49083 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49089 if(this.indicatorpos == 'right') {
49090 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49097 if(align == 'left') {
49105 if(this.labelWidth > 12){
49106 label.style = "width: " + this.labelWidth + 'px';
49108 if(this.labelWidth < 13 && this.labelmd == 0){
49109 this.labelmd = this.labelWidth;
49111 if(this.labellg > 0){
49112 label.cls += ' col-lg-' + this.labellg;
49113 input.cls += ' col-lg-' + (12 - this.labellg);
49115 if(this.labelmd > 0){
49116 label.cls += ' col-md-' + this.labelmd;
49117 container.cls += ' col-md-' + (12 - this.labelmd);
49119 if(this.labelsm > 0){
49120 label.cls += ' col-sm-' + this.labelsm;
49121 container.cls += ' col-sm-' + (12 - this.labelsm);
49123 if(this.labelxs > 0){
49124 label.cls += ' col-xs-' + this.labelxs;
49125 container.cls += ' col-xs-' + (12 - this.labelxs);
49136 var settings = this;
49138 ['xs','sm','md','lg'].map(function(size){
49139 if (settings[size]) {
49140 cfg.cls += ' col-' + size + '-' + settings[size];
49147 initEvents : function()
49149 this.indicator = this.indicatorEl();
49151 this.initCurrencyEvent();
49153 this.initNumberEvent();
49156 initCurrencyEvent : function()
49159 throw "can not find store for combo";
49162 this.store = Roo.factory(this.store, Roo.data);
49163 this.store.parent = this;
49167 this.triggerEl = this.el.select('.input-group-addon', true).first();
49169 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49174 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49175 _this.list.setWidth(lw);
49178 this.list.on('mouseover', this.onViewOver, this);
49179 this.list.on('mousemove', this.onViewMove, this);
49180 this.list.on('scroll', this.onViewScroll, this);
49183 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49186 this.view = new Roo.View(this.list, this.tpl, {
49187 singleSelect:true, store: this.store, selectedClass: this.selectedClass
49190 this.view.on('click', this.onViewClick, this);
49192 this.store.on('beforeload', this.onBeforeLoad, this);
49193 this.store.on('load', this.onLoad, this);
49194 this.store.on('loadexception', this.onLoadException, this);
49196 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49197 "up" : function(e){
49198 this.inKeyMode = true;
49202 "down" : function(e){
49203 if(!this.isExpanded()){
49204 this.onTriggerClick();
49206 this.inKeyMode = true;
49211 "enter" : function(e){
49214 if(this.fireEvent("specialkey", this, e)){
49215 this.onViewClick(false);
49221 "esc" : function(e){
49225 "tab" : function(e){
49228 if(this.fireEvent("specialkey", this, e)){
49229 this.onViewClick(false);
49237 doRelay : function(foo, bar, hname){
49238 if(hname == 'down' || this.scope.isExpanded()){
49239 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49247 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49251 initNumberEvent : function(e)
49253 this.inputEl().on("keydown" , this.fireKey, this);
49254 this.inputEl().on("focus", this.onFocus, this);
49255 this.inputEl().on("blur", this.onBlur, this);
49257 this.inputEl().relayEvent('keyup', this);
49259 if(this.indicator){
49260 this.indicator.addClass('invisible');
49263 this.originalValue = this.getValue();
49265 if(this.validationEvent == 'keyup'){
49266 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49267 this.inputEl().on('keyup', this.filterValidation, this);
49269 else if(this.validationEvent !== false){
49270 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49273 if(this.selectOnFocus){
49274 this.on("focus", this.preFocus, this);
49277 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49278 this.inputEl().on("keypress", this.filterKeys, this);
49280 this.inputEl().relayEvent('keypress', this);
49283 var allowed = "0123456789";
49285 if(this.allowDecimals){
49286 allowed += this.decimalSeparator;
49289 if(this.allowNegative){
49293 if(this.thousandsDelimiter) {
49297 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49299 var keyPress = function(e){
49301 var k = e.getKey();
49303 var c = e.getCharCode();
49306 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49307 allowed.indexOf(String.fromCharCode(c)) === -1
49313 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49317 if(allowed.indexOf(String.fromCharCode(c)) === -1){
49322 this.inputEl().on("keypress", keyPress, this);
49326 onTriggerClick : function(e)
49333 this.loadNext = false;
49335 if(this.isExpanded()){
49340 this.hasFocus = true;
49342 if(this.triggerAction == 'all') {
49343 this.doQuery(this.allQuery, true);
49347 this.doQuery(this.getRawValue());
49350 getCurrency : function()
49352 var v = this.currencyEl().getValue();
49357 restrictHeight : function()
49359 this.list.alignTo(this.currencyEl(), this.listAlign);
49360 this.list.alignTo(this.currencyEl(), this.listAlign);
49363 onViewClick : function(view, doFocus, el, e)
49365 var index = this.view.getSelectedIndexes()[0];
49367 var r = this.store.getAt(index);
49370 this.onSelect(r, index);
49374 onSelect : function(record, index){
49376 if(this.fireEvent('beforeselect', this, record, index) !== false){
49378 this.setFromCurrencyData(index > -1 ? record.data : false);
49382 this.fireEvent('select', this, record, index);
49386 setFromCurrencyData : function(o)
49390 this.lastCurrency = o;
49392 if (this.currencyField) {
49393 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49395 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
49398 this.lastSelectionText = currency;
49400 //setting default currency
49401 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49402 this.setCurrency(this.defaultCurrency);
49406 this.setCurrency(currency);
49409 setFromData : function(o)
49413 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49415 this.setFromCurrencyData(c);
49420 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49422 Roo.log('no value set for '+ (this.name ? this.name : this.id));
49425 this.setValue(value);
49429 setCurrency : function(v)
49431 this.currencyValue = v;
49434 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49439 setValue : function(v)
49441 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49447 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49449 this.inputEl().dom.value = (v == '') ? '' :
49450 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49452 if(!this.allowZero && v === '0') {
49453 this.hiddenEl().dom.value = '';
49454 this.inputEl().dom.value = '';
49461 getRawValue : function()
49463 var v = this.inputEl().getValue();
49468 getValue : function()
49470 return this.fixPrecision(this.parseValue(this.getRawValue()));
49473 parseValue : function(value)
49475 if(this.thousandsDelimiter) {
49477 r = new RegExp(",", "g");
49478 value = value.replace(r, "");
49481 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49482 return isNaN(value) ? '' : value;
49486 fixPrecision : function(value)
49488 if(this.thousandsDelimiter) {
49490 r = new RegExp(",", "g");
49491 value = value.replace(r, "");
49494 var nan = isNaN(value);
49496 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49497 return nan ? '' : value;
49499 return parseFloat(value).toFixed(this.decimalPrecision);
49502 decimalPrecisionFcn : function(v)
49504 return Math.floor(v);
49507 validateValue : function(value)
49509 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49513 var num = this.parseValue(value);
49516 this.markInvalid(String.format(this.nanText, value));
49520 if(num < this.minValue){
49521 this.markInvalid(String.format(this.minText, this.minValue));
49525 if(num > this.maxValue){
49526 this.markInvalid(String.format(this.maxText, this.maxValue));
49533 validate : function()
49535 if(this.disabled || this.allowBlank){
49540 var currency = this.getCurrency();
49542 if(this.validateValue(this.getRawValue()) && currency.length){
49547 this.markInvalid();
49551 getName: function()
49556 beforeBlur : function()
49562 var v = this.parseValue(this.getRawValue());
49569 onBlur : function()
49573 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49574 //this.el.removeClass(this.focusClass);
49577 this.hasFocus = false;
49579 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49583 var v = this.getValue();
49585 if(String(v) !== String(this.startValue)){
49586 this.fireEvent('change', this, v, this.startValue);
49589 this.fireEvent("blur", this);
49592 inputEl : function()
49594 return this.el.select('.roo-money-amount-input', true).first();
49597 currencyEl : function()
49599 return this.el.select('.roo-money-currency-input', true).first();
49602 hiddenEl : function()
49604 return this.el.select('input.hidden-number-input',true).first();
49608 * @class Roo.bootstrap.BezierSignature
49609 * @extends Roo.bootstrap.Component
49610 * Bootstrap BezierSignature class
49611 * This script refer to:
49612 * Title: Signature Pad
49614 * Availability: https://github.com/szimek/signature_pad
49617 * Create a new BezierSignature
49618 * @param {Object} config The config object
49621 Roo.bootstrap.BezierSignature = function(config){
49622 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49628 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49635 mouse_btn_down: true,
49638 * @cfg {int} canvas height
49640 canvas_height: '200px',
49643 * @cfg {float|function} Radius of a single dot.
49648 * @cfg {float} Minimum width of a line. Defaults to 0.5.
49653 * @cfg {float} Maximum width of a line. Defaults to 2.5.
49658 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49663 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49668 * @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.
49670 bg_color: 'rgba(0, 0, 0, 0)',
49673 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49675 dot_color: 'black',
49678 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49680 velocity_filter_weight: 0.7,
49683 * @cfg {function} Callback when stroke begin.
49688 * @cfg {function} Callback when stroke end.
49692 getAutoCreate : function()
49694 var cls = 'roo-signature column';
49697 cls += ' ' + this.cls;
49707 for(var i = 0; i < col_sizes.length; i++) {
49708 if(this[col_sizes[i]]) {
49709 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49719 cls: 'roo-signature-body',
49723 cls: 'roo-signature-body-canvas',
49724 height: this.canvas_height,
49725 width: this.canvas_width
49732 style: 'display: none'
49740 initEvents: function()
49742 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49744 var canvas = this.canvasEl();
49746 // mouse && touch event swapping...
49747 canvas.dom.style.touchAction = 'none';
49748 canvas.dom.style.msTouchAction = 'none';
49750 this.mouse_btn_down = false;
49751 canvas.on('mousedown', this._handleMouseDown, this);
49752 canvas.on('mousemove', this._handleMouseMove, this);
49753 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49755 if (window.PointerEvent) {
49756 canvas.on('pointerdown', this._handleMouseDown, this);
49757 canvas.on('pointermove', this._handleMouseMove, this);
49758 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49761 if ('ontouchstart' in window) {
49762 canvas.on('touchstart', this._handleTouchStart, this);
49763 canvas.on('touchmove', this._handleTouchMove, this);
49764 canvas.on('touchend', this._handleTouchEnd, this);
49767 Roo.EventManager.onWindowResize(this.resize, this, true);
49769 // file input event
49770 this.fileEl().on('change', this.uploadImage, this);
49777 resize: function(){
49779 var canvas = this.canvasEl().dom;
49780 var ctx = this.canvasElCtx();
49781 var img_data = false;
49783 if(canvas.width > 0) {
49784 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49786 // setting canvas width will clean img data
49789 var style = window.getComputedStyle ?
49790 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49792 var padding_left = parseInt(style.paddingLeft) || 0;
49793 var padding_right = parseInt(style.paddingRight) || 0;
49795 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49798 ctx.putImageData(img_data, 0, 0);
49802 _handleMouseDown: function(e)
49804 if (e.browserEvent.which === 1) {
49805 this.mouse_btn_down = true;
49806 this.strokeBegin(e);
49810 _handleMouseMove: function (e)
49812 if (this.mouse_btn_down) {
49813 this.strokeMoveUpdate(e);
49817 _handleMouseUp: function (e)
49819 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49820 this.mouse_btn_down = false;
49825 _handleTouchStart: function (e) {
49827 e.preventDefault();
49828 if (e.browserEvent.targetTouches.length === 1) {
49829 // var touch = e.browserEvent.changedTouches[0];
49830 // this.strokeBegin(touch);
49832 this.strokeBegin(e); // assume e catching the correct xy...
49836 _handleTouchMove: function (e) {
49837 e.preventDefault();
49838 // var touch = event.targetTouches[0];
49839 // _this._strokeMoveUpdate(touch);
49840 this.strokeMoveUpdate(e);
49843 _handleTouchEnd: function (e) {
49844 var wasCanvasTouched = e.target === this.canvasEl().dom;
49845 if (wasCanvasTouched) {
49846 e.preventDefault();
49847 // var touch = event.changedTouches[0];
49848 // _this._strokeEnd(touch);
49853 reset: function () {
49854 this._lastPoints = [];
49855 this._lastVelocity = 0;
49856 this._lastWidth = (this.min_width + this.max_width) / 2;
49857 this.canvasElCtx().fillStyle = this.dot_color;
49860 strokeMoveUpdate: function(e)
49862 this.strokeUpdate(e);
49864 if (this.throttle) {
49865 this.throttleStroke(this.strokeUpdate, this.throttle);
49868 this.strokeUpdate(e);
49872 strokeBegin: function(e)
49874 var newPointGroup = {
49875 color: this.dot_color,
49879 if (typeof this.onBegin === 'function') {
49883 this.curve_data.push(newPointGroup);
49885 this.strokeUpdate(e);
49888 strokeUpdate: function(e)
49890 var rect = this.canvasEl().dom.getBoundingClientRect();
49891 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49892 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49893 var lastPoints = lastPointGroup.points;
49894 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49895 var isLastPointTooClose = lastPoint
49896 ? point.distanceTo(lastPoint) <= this.min_distance
49898 var color = lastPointGroup.color;
49899 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49900 var curve = this.addPoint(point);
49902 this.drawDot({color: color, point: point});
49905 this.drawCurve({color: color, curve: curve});
49915 strokeEnd: function(e)
49917 this.strokeUpdate(e);
49918 if (typeof this.onEnd === 'function') {
49923 addPoint: function (point) {
49924 var _lastPoints = this._lastPoints;
49925 _lastPoints.push(point);
49926 if (_lastPoints.length > 2) {
49927 if (_lastPoints.length === 3) {
49928 _lastPoints.unshift(_lastPoints[0]);
49930 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49931 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49932 _lastPoints.shift();
49938 calculateCurveWidths: function (startPoint, endPoint) {
49939 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49940 (1 - this.velocity_filter_weight) * this._lastVelocity;
49942 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49945 start: this._lastWidth
49948 this._lastVelocity = velocity;
49949 this._lastWidth = newWidth;
49953 drawDot: function (_a) {
49954 var color = _a.color, point = _a.point;
49955 var ctx = this.canvasElCtx();
49956 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49958 this.drawCurveSegment(point.x, point.y, width);
49960 ctx.fillStyle = color;
49964 drawCurve: function (_a) {
49965 var color = _a.color, curve = _a.curve;
49966 var ctx = this.canvasElCtx();
49967 var widthDelta = curve.endWidth - curve.startWidth;
49968 var drawSteps = Math.floor(curve.length()) * 2;
49970 ctx.fillStyle = color;
49971 for (var i = 0; i < drawSteps; i += 1) {
49972 var t = i / drawSteps;
49978 var x = uuu * curve.startPoint.x;
49979 x += 3 * uu * t * curve.control1.x;
49980 x += 3 * u * tt * curve.control2.x;
49981 x += ttt * curve.endPoint.x;
49982 var y = uuu * curve.startPoint.y;
49983 y += 3 * uu * t * curve.control1.y;
49984 y += 3 * u * tt * curve.control2.y;
49985 y += ttt * curve.endPoint.y;
49986 var width = curve.startWidth + ttt * widthDelta;
49987 this.drawCurveSegment(x, y, width);
49993 drawCurveSegment: function (x, y, width) {
49994 var ctx = this.canvasElCtx();
49996 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49997 this.is_empty = false;
50002 var ctx = this.canvasElCtx();
50003 var canvas = this.canvasEl().dom;
50004 ctx.fillStyle = this.bg_color;
50005 ctx.clearRect(0, 0, canvas.width, canvas.height);
50006 ctx.fillRect(0, 0, canvas.width, canvas.height);
50007 this.curve_data = [];
50009 this.is_empty = true;
50014 return this.el.select('input',true).first();
50017 canvasEl: function()
50019 return this.el.select('canvas',true).first();
50022 canvasElCtx: function()
50024 return this.el.select('canvas',true).first().dom.getContext('2d');
50027 getImage: function(type)
50029 if(this.is_empty) {
50034 return this.canvasEl().dom.toDataURL('image/'+type, 1);
50037 drawFromImage: function(img_src)
50039 var img = new Image();
50041 img.onload = function(){
50042 this.canvasElCtx().drawImage(img, 0, 0);
50047 this.is_empty = false;
50050 selectImage: function()
50052 this.fileEl().dom.click();
50055 uploadImage: function(e)
50057 var reader = new FileReader();
50059 reader.onload = function(e){
50060 var img = new Image();
50061 img.onload = function(){
50063 this.canvasElCtx().drawImage(img, 0, 0);
50065 img.src = e.target.result;
50068 reader.readAsDataURL(e.target.files[0]);
50071 // Bezier Point Constructor
50072 Point: (function () {
50073 function Point(x, y, time) {
50076 this.time = time || Date.now();
50078 Point.prototype.distanceTo = function (start) {
50079 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50081 Point.prototype.equals = function (other) {
50082 return this.x === other.x && this.y === other.y && this.time === other.time;
50084 Point.prototype.velocityFrom = function (start) {
50085 return this.time !== start.time
50086 ? this.distanceTo(start) / (this.time - start.time)
50093 // Bezier Constructor
50094 Bezier: (function () {
50095 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50096 this.startPoint = startPoint;
50097 this.control2 = control2;
50098 this.control1 = control1;
50099 this.endPoint = endPoint;
50100 this.startWidth = startWidth;
50101 this.endWidth = endWidth;
50103 Bezier.fromPoints = function (points, widths, scope) {
50104 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50105 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50106 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50108 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50109 var dx1 = s1.x - s2.x;
50110 var dy1 = s1.y - s2.y;
50111 var dx2 = s2.x - s3.x;
50112 var dy2 = s2.y - s3.y;
50113 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50114 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50115 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50116 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50117 var dxm = m1.x - m2.x;
50118 var dym = m1.y - m2.y;
50119 var k = l2 / (l1 + l2);
50120 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50121 var tx = s2.x - cm.x;
50122 var ty = s2.y - cm.y;
50124 c1: new scope.Point(m1.x + tx, m1.y + ty),
50125 c2: new scope.Point(m2.x + tx, m2.y + ty)
50128 Bezier.prototype.length = function () {
50133 for (var i = 0; i <= steps; i += 1) {
50135 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50136 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50138 var xdiff = cx - px;
50139 var ydiff = cy - py;
50140 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50147 Bezier.prototype.point = function (t, start, c1, c2, end) {
50148 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50149 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50150 + (3.0 * c2 * (1.0 - t) * t * t)
50151 + (end * t * t * t);
50156 throttleStroke: function(fn, wait) {
50157 if (wait === void 0) { wait = 250; }
50159 var timeout = null;
50163 var later = function () {
50164 previous = Date.now();
50166 result = fn.apply(storedContext, storedArgs);
50168 storedContext = null;
50172 return function wrapper() {
50174 for (var _i = 0; _i < arguments.length; _i++) {
50175 args[_i] = arguments[_i];
50177 var now = Date.now();
50178 var remaining = wait - (now - previous);
50179 storedContext = this;
50181 if (remaining <= 0 || remaining > wait) {
50183 clearTimeout(timeout);
50187 result = fn.apply(storedContext, storedArgs);
50189 storedContext = null;
50193 else if (!timeout) {
50194 timeout = window.setTimeout(later, remaining);
50204 // old names for form elements
50205 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
50206 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
50207 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
50208 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
50209 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
50210 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
50211 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
50212 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
50213 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
50214 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
50215 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
50216 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
50217 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
50218 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
50219 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
50220 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
50221 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
50222 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
50223 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
50224 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
50225 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
50226 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
50227 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
50228 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
50229 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
50230 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
50232 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
50233 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50235 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
50236 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
50238 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
50239 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50240 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
50241 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator