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)
393 cn = Roo.factory(tree);
394 //Roo.log(['addxtype', cn]);
396 cn.parentType = this.xtype; //??
397 cn.parentId = this.id;
401 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
402 if (typeof(cn.container_method) == 'string') {
403 cntr = cn.container_method;
407 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
409 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
411 var build_from_html = Roo.XComponent.build_from_html;
413 var is_body = (tree.xtype == 'Body') ;
415 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
417 var self_cntr_el = Roo.get(this[cntr](false));
419 // do not try and build conditional elements
420 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
424 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
425 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
426 return this.addxtypeChild(tree,cntr, is_body);
429 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
432 return this.addxtypeChild(Roo.apply({}, tree),cntr);
435 Roo.log('skipping render');
441 if (!build_from_html) {
445 // this i think handles overlaying multiple children of the same type
446 // with the sam eelement.. - which might be buggy..
448 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
454 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
458 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
465 addxtypeChild : function (tree, cntr, is_body)
467 Roo.debug && Roo.log('addxtypeChild:' + cntr);
468 Roo.log('ADDXTYPECHILD');
470 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
473 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
474 (typeof(tree['flexy:foreach']) != 'undefined');
478 skip_children = false;
479 // render the element if it's not BODY.
482 // if parent was disabled, then do not try and create the children..
483 if(!this[cntr](true)){
488 cn = Roo.factory(tree);
491 cn.parentType = this.xtype; //??
492 cn.parentId = this.id;
494 var build_from_html = Roo.XComponent.build_from_html;
497 // does the container contain child eleemnts with 'xtype' attributes.
498 // that match this xtype..
499 // note - when we render we create these as well..
500 // so we should check to see if body has xtype set.
501 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
503 var self_cntr_el = Roo.get(this[cntr](false));
504 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
506 //Roo.log(Roo.XComponent.build_from_html);
507 //Roo.log("got echild:");
510 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
511 // and are not displayed -this causes this to use up the wrong element when matching.
512 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
517 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
518 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
525 //echild.dom.removeAttribute('xtype');
527 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
528 Roo.debug && Roo.log(self_cntr_el);
529 Roo.debug && Roo.log(echild);
530 Roo.debug && Roo.log(cn);
536 // if object has flexy:if - then it may or may not be rendered.
537 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
538 // skip a flexy if element.
539 Roo.debug && Roo.log('skipping render');
540 Roo.debug && Roo.log(tree);
542 Roo.debug && Roo.log('skipping all children');
543 skip_children = true;
548 // actually if flexy:foreach is found, we really want to create
549 // multiple copies here...
551 //Roo.log(this[cntr]());
552 // some elements do not have render methods.. like the layouts...
554 if(this[cntr](true) === false){
559 cn.render && cn.render(this[cntr](true));
562 // then add the element..
569 if (typeof (tree.menu) != 'undefined') {
570 tree.menu.parentType = cn.xtype;
571 tree.menu.triggerEl = cn.el;
572 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
576 if (!tree.items || !tree.items.length) {
578 //Roo.log(["no children", this]);
583 var items = tree.items;
586 //Roo.log(items.length);
588 if (!skip_children) {
589 for(var i =0;i < items.length;i++) {
590 // Roo.log(['add child', items[i]]);
591 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
597 //Roo.log("fire childrenrendered");
599 cn.fireEvent('childrenrendered', this);
605 * Set the element that will be used to show or hide
607 setVisibilityEl : function(el)
609 this.visibilityEl = el;
613 * Get the element that will be used to show or hide
615 getVisibilityEl : function()
617 if (typeof(this.visibilityEl) == 'object') {
618 return this.visibilityEl;
621 if (typeof(this.visibilityEl) == 'string') {
622 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
629 * Show a component - removes 'hidden' class
633 if(!this.getVisibilityEl()){
637 this.getVisibilityEl().removeClass(['hidden','d-none']);
639 this.fireEvent('show', this);
644 * Hide a component - adds 'hidden' class
648 if(!this.getVisibilityEl()){
652 this.getVisibilityEl().addClass(['hidden','d-none']);
654 this.fireEvent('hide', this);
667 * @class Roo.bootstrap.Element
668 * @extends Roo.bootstrap.Component
669 * @children Roo.bootstrap.Component
670 * Bootstrap Element class (basically a DIV used to make random stuff )
672 * @cfg {String} html contents of the element
673 * @cfg {String} tag tag of the element
674 * @cfg {String} cls class of the element
675 * @cfg {Boolean} preventDefault (true|false) default false
676 * @cfg {Boolean} clickable (true|false) default false
677 * @cfg {String} role default blank - set to button to force cursor pointer
681 * Create a new Element
682 * @param {Object} config The config object
685 Roo.bootstrap.Element = function(config){
686 Roo.bootstrap.Element.superclass.constructor.call(this, config);
692 * When a element is chick
693 * @param {Roo.bootstrap.Element} this
694 * @param {Roo.EventObject} e
702 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
707 preventDefault: false,
712 getAutoCreate : function(){
716 // cls: this.cls, double assign in parent class Component.js :: onRender
719 if (this.role !== false) {
720 cfg.role = this.role;
726 initEvents: function()
728 Roo.bootstrap.Element.superclass.initEvents.call(this);
731 this.el.on('click', this.onClick, this);
737 onClick : function(e)
739 if(this.preventDefault){
743 this.fireEvent('click', this, e); // why was this double click before?
751 getValue : function()
753 return this.el.dom.innerHTML;
756 setValue : function(value)
758 this.el.dom.innerHTML = value;
773 * @class Roo.bootstrap.DropTarget
774 * @extends Roo.bootstrap.Element
775 * Bootstrap DropTarget class
777 * @cfg {string} name dropable name
780 * Create a new Dropable Area
781 * @param {Object} config The config object
784 Roo.bootstrap.DropTarget = function(config){
785 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
791 * When a element is chick
792 * @param {Roo.bootstrap.Element} this
793 * @param {Roo.EventObject} e
799 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
802 getAutoCreate : function(){
807 initEvents: function()
809 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
810 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
813 drop : this.dragDrop.createDelegate(this),
814 enter : this.dragEnter.createDelegate(this),
815 out : this.dragOut.createDelegate(this),
816 over : this.dragOver.createDelegate(this)
820 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
823 dragDrop : function(source,e,data)
825 // user has to decide how to impliment this.
828 //this.fireEvent('drop', this, source, e ,data);
832 dragEnter : function(n, dd, e, data)
834 // probably want to resize the element to match the dropped element..
836 this.originalSize = this.el.getSize();
837 this.el.setSize( n.el.getSize());
838 this.dropZone.DDM.refreshCache(this.name);
839 Roo.log([n, dd, e, data]);
842 dragOut : function(value)
844 // resize back to normal
846 this.el.setSize(this.originalSize);
847 this.dropZone.resetConstraints();
850 dragOver : function()
867 * @class Roo.bootstrap.Body
868 * @extends Roo.bootstrap.Component
869 * @children Roo.bootstrap.Component
870 * @parent none builder
871 * Bootstrap Body class
875 * @param {Object} config The config object
878 Roo.bootstrap.Body = function(config){
880 config = config || {};
882 Roo.bootstrap.Body.superclass.constructor.call(this, config);
883 this.el = Roo.get(config.el ? config.el : document.body );
884 if (this.cls && this.cls.length) {
885 Roo.get(document.body).addClass(this.cls);
889 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
891 is_body : true,// just to make sure it's constructed?
896 onRender : function(ct, position)
898 /* Roo.log("Roo.bootstrap.Body - onRender");
899 if (this.cls && this.cls.length) {
900 Roo.get(document.body).addClass(this.cls);
919 * @class Roo.bootstrap.ButtonGroup
920 * @extends Roo.bootstrap.Component
921 * Bootstrap ButtonGroup class
922 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
924 * @cfg {String} size lg | sm | xs (default empty normal)
925 * @cfg {String} align vertical | justified (default none)
926 * @cfg {String} direction up | down (default down)
927 * @cfg {Boolean} toolbar false | true
928 * @cfg {Boolean} btn true | false
933 * @param {Object} config The config object
936 Roo.bootstrap.ButtonGroup = function(config){
937 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
940 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
948 getAutoCreate : function(){
954 cfg.html = this.html || cfg.html;
965 if (['vertical','justified'].indexOf(this.align)!==-1) {
966 cfg.cls = 'btn-group-' + this.align;
968 if (this.align == 'justified') {
969 console.log(this.items);
973 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
974 cfg.cls += ' btn-group-' + this.size;
977 if (this.direction == 'up') {
978 cfg.cls += ' dropup' ;
984 * Add a button to the group (similar to NavItem API.)
986 addItem : function(cfg)
988 var cn = new Roo.bootstrap.Button(cfg);
990 cn.parentId = this.id;
991 cn.onRender(this.el, null);
1005 * @class Roo.bootstrap.Button
1006 * @extends Roo.bootstrap.Component
1007 * Bootstrap Button class
1008 * @cfg {String} html The button content
1009 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1010 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1011 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1012 * @cfg {String} size (lg|sm|xs)
1013 * @cfg {String} tag (a|input|submit)
1014 * @cfg {String} href empty or href
1015 * @cfg {Boolean} disabled default false;
1016 * @cfg {Boolean} isClose default false;
1017 * @cfg {String} glyphicon depricated - use fa
1018 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1019 * @cfg {String} badge text for badge
1020 * @cfg {String} theme (default|glow)
1021 * @cfg {Boolean} inverse dark themed version
1022 * @cfg {Boolean} toggle is it a slidy toggle button
1023 * @cfg {Boolean} pressed default null - if the button ahs active state
1024 * @cfg {String} ontext text for on slidy toggle state
1025 * @cfg {String} offtext text for off slidy toggle state
1026 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1027 * @cfg {Boolean} removeClass remove the standard class..
1028 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1029 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1030 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1033 * Create a new button
1034 * @param {Object} config The config object
1038 Roo.bootstrap.Button = function(config){
1039 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1045 * When a button is pressed
1046 * @param {Roo.bootstrap.Button} btn
1047 * @param {Roo.EventObject} e
1052 * When a button is double clicked
1053 * @param {Roo.bootstrap.Button} btn
1054 * @param {Roo.EventObject} e
1059 * After the button has been toggles
1060 * @param {Roo.bootstrap.Button} btn
1061 * @param {Roo.EventObject} e
1062 * @param {boolean} pressed (also available as button.pressed)
1068 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1089 preventDefault: true,
1098 getAutoCreate : function(){
1106 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1107 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1108 this.tag = 'button';
1112 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1114 if (this.toggle == true) {
1117 cls: 'slider-frame roo-button',
1121 'data-on-text':'ON',
1122 'data-off-text':'OFF',
1123 cls: 'slider-button',
1128 // why are we validating the weights?
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130 cfg.cls += ' ' + this.weight;
1137 cfg.cls += ' close';
1139 cfg["aria-hidden"] = true;
1141 cfg.html = "×";
1147 if (this.theme==='default') {
1148 cfg.cls = 'btn roo-button';
1150 //if (this.parentType != 'Navbar') {
1151 this.weight = this.weight.length ? this.weight : 'default';
1153 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1155 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1156 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1157 cfg.cls += ' btn-' + outline + weight;
1158 if (this.weight == 'default') {
1160 cfg.cls += ' btn-' + this.weight;
1163 } else if (this.theme==='glow') {
1166 cfg.cls = 'btn-glow roo-button';
1168 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1170 cfg.cls += ' ' + this.weight;
1176 this.cls += ' inverse';
1180 if (this.active || this.pressed === true) {
1181 cfg.cls += ' active';
1184 if (this.disabled) {
1185 cfg.disabled = 'disabled';
1189 Roo.log('changing to ul' );
1191 this.glyphicon = 'caret';
1192 if (Roo.bootstrap.version == 4) {
1193 this.fa = 'caret-down';
1198 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1200 //gsRoo.log(this.parentType);
1201 if (this.parentType === 'Navbar' && !this.parent().bar) {
1202 Roo.log('changing to li?');
1211 href : this.href || '#'
1214 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1215 cfg.cls += ' dropdown';
1222 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1224 if (this.glyphicon) {
1225 cfg.html = ' ' + cfg.html;
1230 cls: 'glyphicon glyphicon-' + this.glyphicon
1235 cfg.html = ' ' + cfg.html;
1240 cls: 'fa fas fa-' + this.fa
1250 // cfg.cls='btn roo-button';
1254 var value = cfg.html;
1259 cls: 'glyphicon glyphicon-' + this.glyphicon,
1266 cls: 'fa fas fa-' + this.fa,
1271 var bw = this.badge_weight.length ? this.badge_weight :
1272 (this.weight.length ? this.weight : 'secondary');
1273 bw = bw == 'default' ? 'secondary' : bw;
1279 cls: 'badge badge-' + bw,
1288 cfg.cls += ' dropdown';
1289 cfg.html = typeof(cfg.html) != 'undefined' ?
1290 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1293 if (cfg.tag !== 'a' && this.href !== '') {
1294 throw "Tag must be a to set href.";
1295 } else if (this.href.length > 0) {
1296 cfg.href = this.href;
1299 if(this.removeClass){
1304 cfg.target = this.target;
1309 initEvents: function() {
1310 // Roo.log('init events?');
1311 // Roo.log(this.el.dom);
1314 if (typeof (this.menu) != 'undefined') {
1315 this.menu.parentType = this.xtype;
1316 this.menu.triggerEl = this.el;
1317 this.addxtype(Roo.apply({}, this.menu));
1321 if (this.el.hasClass('roo-button')) {
1322 this.el.on('click', this.onClick, this);
1323 this.el.on('dblclick', this.onDblClick, this);
1325 this.el.select('.roo-button').on('click', this.onClick, this);
1326 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1330 if(this.removeClass){
1331 this.el.on('click', this.onClick, this);
1334 if (this.group === true) {
1335 if (this.pressed === false || this.pressed === true) {
1338 this.pressed = false;
1339 this.setActive(this.pressed);
1344 this.el.enableDisplayMode();
1347 onClick : function(e)
1349 if (this.disabled) {
1353 Roo.log('button on click ');
1354 if(this.href === '' || this.preventDefault){
1363 this.setActive(true);
1364 var pi = this.parent().items;
1365 for (var i = 0;i < pi.length;i++) {
1366 if (this == pi[i]) {
1369 if (pi[i].el.hasClass('roo-button')) {
1370 pi[i].setActive(false);
1373 this.fireEvent('click', this, e);
1377 if (this.pressed === true || this.pressed === false) {
1378 this.toggleActive(e);
1382 this.fireEvent('click', this, e);
1384 onDblClick: function(e)
1386 if (this.disabled) {
1389 if(this.preventDefault){
1392 this.fireEvent('dblclick', this, e);
1395 * Enables this button
1399 this.disabled = false;
1400 this.el.removeClass('disabled');
1401 this.el.dom.removeAttribute("disabled");
1405 * Disable this button
1407 disable : function()
1409 this.disabled = true;
1410 this.el.addClass('disabled');
1411 this.el.attr("disabled", "disabled")
1414 * sets the active state on/off,
1415 * @param {Boolean} state (optional) Force a particular state
1417 setActive : function(v) {
1419 this.el[v ? 'addClass' : 'removeClass']('active');
1423 * toggles the current active state
1425 toggleActive : function(e)
1427 this.setActive(!this.pressed); // this modifies pressed...
1428 this.fireEvent('toggle', this, e, this.pressed);
1431 * get the current active state
1432 * @return {boolean} true if it's active
1434 isActive : function()
1436 return this.el.hasClass('active');
1439 * set the text of the first selected button
1441 setText : function(str)
1443 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1446 * get the text of the first selected button
1448 getText : function()
1450 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1453 setWeight : function(str)
1455 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1456 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1458 var outline = this.outline ? 'outline-' : '';
1459 if (str == 'default') {
1460 this.el.addClass('btn-default btn-outline-secondary');
1463 this.el.addClass('btn-' + outline + str);
1468 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1470 Roo.bootstrap.Button.weights = [
1490 * @class Roo.bootstrap.Column
1491 * @extends Roo.bootstrap.Component
1492 * @children Roo.bootstrap.Component
1493 * Bootstrap Column class
1494 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1495 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1496 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1497 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1498 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1499 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1500 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1501 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1504 * @cfg {Boolean} hidden (true|false) hide the element
1505 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1506 * @cfg {String} fa (ban|check|...) font awesome icon
1507 * @cfg {Number} fasize (1|2|....) font awsome size
1509 * @cfg {String} icon (info-sign|check|...) glyphicon name
1511 * @cfg {String} html content of column.
1514 * Create a new Column
1515 * @param {Object} config The config object
1518 Roo.bootstrap.Column = function(config){
1519 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1522 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1540 getAutoCreate : function(){
1541 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1549 var sizes = ['xs','sm','md','lg'];
1550 sizes.map(function(size ,ix){
1551 //Roo.log( size + ':' + settings[size]);
1553 if (settings[size+'off'] !== false) {
1554 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1557 if (settings[size] === false) {
1561 if (!settings[size]) { // 0 = hidden
1562 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1564 for (var i = ix; i > -1; i--) {
1565 cfg.cls += ' d-' + sizes[i] + '-none';
1571 cfg.cls += ' col-' + size + '-' + settings[size] + (
1572 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1578 cfg.cls += ' hidden';
1581 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1582 cfg.cls +=' alert alert-' + this.alert;
1586 if (this.html.length) {
1587 cfg.html = this.html;
1591 if (this.fasize > 1) {
1592 fasize = ' fa-' + this.fasize + 'x';
1594 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1599 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1618 * @class Roo.bootstrap.Container
1619 * @extends Roo.bootstrap.Component
1620 * @children Roo.bootstrap.Component
1622 * Bootstrap Container class
1623 * @cfg {Boolean} jumbotron is it a jumbotron element
1624 * @cfg {String} html content of element
1625 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1626 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1627 * @cfg {String} header content of header (for panel)
1628 * @cfg {String} footer content of footer (for panel)
1629 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1630 * @cfg {String} tag (header|aside|section) type of HTML tag.
1631 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1632 * @cfg {String} fa font awesome icon
1633 * @cfg {String} icon (info-sign|check|...) glyphicon name
1634 * @cfg {Boolean} hidden (true|false) hide the element
1635 * @cfg {Boolean} expandable (true|false) default false
1636 * @cfg {Boolean} expanded (true|false) default true
1637 * @cfg {String} rheader contet on the right of header
1638 * @cfg {Boolean} clickable (true|false) default false
1642 * Create a new Container
1643 * @param {Object} config The config object
1646 Roo.bootstrap.Container = function(config){
1647 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1653 * After the panel has been expand
1655 * @param {Roo.bootstrap.Container} this
1660 * After the panel has been collapsed
1662 * @param {Roo.bootstrap.Container} this
1667 * When a element is chick
1668 * @param {Roo.bootstrap.Container} this
1669 * @param {Roo.EventObject} e
1675 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1693 getChildContainer : function() {
1699 if (this.panel.length) {
1700 return this.el.select('.panel-body',true).first();
1707 getAutoCreate : function(){
1710 tag : this.tag || 'div',
1714 if (this.jumbotron) {
1715 cfg.cls = 'jumbotron';
1720 // - this is applied by the parent..
1722 // cfg.cls = this.cls + '';
1725 if (this.sticky.length) {
1727 var bd = Roo.get(document.body);
1728 if (!bd.hasClass('bootstrap-sticky')) {
1729 bd.addClass('bootstrap-sticky');
1730 Roo.select('html',true).setStyle('height', '100%');
1733 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1737 if (this.well.length) {
1738 switch (this.well) {
1741 cfg.cls +=' well well-' +this.well;
1750 cfg.cls += ' hidden';
1754 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1755 cfg.cls +=' alert alert-' + this.alert;
1760 if (this.panel.length) {
1761 cfg.cls += ' panel panel-' + this.panel;
1763 if (this.header.length) {
1767 if(this.expandable){
1769 cfg.cls = cfg.cls + ' expandable';
1773 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1781 cls : 'panel-title',
1782 html : (this.expandable ? ' ' : '') + this.header
1786 cls: 'panel-header-right',
1792 cls : 'panel-heading',
1793 style : this.expandable ? 'cursor: pointer' : '',
1801 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1806 if (this.footer.length) {
1808 cls : 'panel-footer',
1817 body.html = this.html || cfg.html;
1818 // prefix with the icons..
1820 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1823 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1828 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1829 cfg.cls = 'container';
1835 initEvents: function()
1837 if(this.expandable){
1838 var headerEl = this.headerEl();
1841 headerEl.on('click', this.onToggleClick, this);
1846 this.el.on('click', this.onClick, this);
1851 onToggleClick : function()
1853 var headerEl = this.headerEl();
1869 if(this.fireEvent('expand', this)) {
1871 this.expanded = true;
1873 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1875 this.el.select('.panel-body',true).first().removeClass('hide');
1877 var toggleEl = this.toggleEl();
1883 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1888 collapse : function()
1890 if(this.fireEvent('collapse', this)) {
1892 this.expanded = false;
1894 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1895 this.el.select('.panel-body',true).first().addClass('hide');
1897 var toggleEl = this.toggleEl();
1903 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1907 toggleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1913 return this.el.select('.panel-heading .fa',true).first();
1916 headerEl : function()
1918 if(!this.el || !this.panel.length || !this.header.length){
1922 return this.el.select('.panel-heading',true).first()
1927 if(!this.el || !this.panel.length){
1931 return this.el.select('.panel-body',true).first()
1934 titleEl : function()
1936 if(!this.el || !this.panel.length || !this.header.length){
1940 return this.el.select('.panel-title',true).first();
1943 setTitle : function(v)
1945 var titleEl = this.titleEl();
1951 titleEl.dom.innerHTML = v;
1954 getTitle : function()
1957 var titleEl = this.titleEl();
1963 return titleEl.dom.innerHTML;
1966 setRightTitle : function(v)
1968 var t = this.el.select('.panel-header-right',true).first();
1974 t.dom.innerHTML = v;
1977 onClick : function(e)
1981 this.fireEvent('click', this, e);
1986 * @class Roo.bootstrap.Card
1987 * @extends Roo.bootstrap.Component
1988 * @children Roo.bootstrap.Component
1990 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1993 * possible... may not be implemented..
1994 * @cfg {String} header_image src url of image.
1995 * @cfg {String|Object} header
1996 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1997 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1999 * @cfg {String} title
2000 * @cfg {String} subtitle
2001 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
2002 * @cfg {String} footer
2004 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
2006 * @cfg {String} margin (0|1|2|3|4|5|auto)
2007 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2008 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2009 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2010 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2011 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2012 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2014 * @cfg {String} padding (0|1|2|3|4|5)
2015 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2016 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2017 * @cfg {String} padding_left (0|1|2|3|4|5)
2018 * @cfg {String} padding_right (0|1|2|3|4|5)
2019 * @cfg {String} padding_x (0|1|2|3|4|5)
2020 * @cfg {String} padding_y (0|1|2|3|4|5)
2022 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2023 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2024 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2025 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2026 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2028 * @config {Boolean} dragable if this card can be dragged.
2029 * @config {String} drag_group group for drag
2030 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2031 * @config {String} drop_group group for drag
2033 * @config {Boolean} collapsable can the body be collapsed.
2034 * @config {Boolean} collapsed is the body collapsed when rendered...
2035 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2036 * @config {Boolean} rotated is the body rotated when rendered...
2039 * Create a new Container
2040 * @param {Object} config The config object
2043 Roo.bootstrap.Card = function(config){
2044 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2050 * When a element a card is dropped
2051 * @param {Roo.bootstrap.Card} this
2054 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2055 * @param {String} position 'above' or 'below'
2056 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2062 * When a element a card is rotate
2063 * @param {Roo.bootstrap.Card} this
2064 * @param {Roo.Element} n the node being dropped?
2065 * @param {Boolean} rotate status
2070 * When a card element is dragged over ready to drop (return false to block dropable)
2071 * @param {Roo.bootstrap.Card} this
2072 * @param {Object} data from dragdrop
2080 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2085 margin: '', /// may be better in component?
2115 collapsable : false,
2124 childContainer : false,
2125 dropEl : false, /// the dom placeholde element that indicates drop location.
2126 containerEl: false, // body container
2127 bodyEl: false, // card-body
2128 headerContainerEl : false, //
2130 header_imageEl : false,
2133 layoutCls : function()
2137 Roo.log(this.margin_bottom.length);
2138 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2139 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2141 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2142 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2144 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2145 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2149 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2150 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2151 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2155 // more generic support?
2163 // Roo.log("Call onRender: " + this.xtype);
2164 /* We are looking at something like this.
2166 <img src="..." class="card-img-top" alt="...">
2167 <div class="card-body">
2168 <h5 class="card-title">Card title</h5>
2169 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2171 >> this bit is really the body...
2172 <div> << we will ad dthis in hopefully it will not break shit.
2174 ** card text does not actually have any styling...
2176 <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>
2179 <a href="#" class="card-link">Card link</a>
2182 <div class="card-footer">
2183 <small class="text-muted">Last updated 3 mins ago</small>
2187 getAutoCreate : function(){
2195 if (this.weight.length && this.weight != 'light') {
2196 cfg.cls += ' text-white';
2198 cfg.cls += ' text-dark'; // need as it's nested..
2200 if (this.weight.length) {
2201 cfg.cls += ' bg-' + this.weight;
2204 cfg.cls += ' ' + this.layoutCls();
2207 var hdr_ctr = false;
2208 if (this.header.length) {
2210 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2211 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2219 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2225 if (this.collapsable) {
2228 cls : 'd-block user-select-none',
2232 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2237 hdr.cn.push(hdr_ctr);
2242 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2247 if (this.header_image.length) {
2250 cls : 'card-img-top',
2251 src: this.header_image // escape?
2256 cls : 'card-img-top d-none'
2262 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2266 if (this.collapsable || this.rotateable) {
2269 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2276 if (this.title.length) {
2280 src: this.title // escape?
2284 if (this.subtitle.length) {
2288 src: this.subtitle // escape?
2294 cls : 'roo-card-body-ctr'
2297 if (this.html.length) {
2303 // fixme ? handle objects?
2305 if (this.footer.length) {
2308 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2313 cfg.cn.push({cls : 'card-footer d-none'});
2322 getCardHeader : function()
2324 var ret = this.el.select('.card-header',true).first();
2325 if (ret.hasClass('d-none')) {
2326 ret.removeClass('d-none');
2331 getCardFooter : function()
2333 var ret = this.el.select('.card-footer',true).first();
2334 if (ret.hasClass('d-none')) {
2335 ret.removeClass('d-none');
2340 getCardImageTop : function()
2342 var ret = this.header_imageEl;
2343 if (ret.hasClass('d-none')) {
2344 ret.removeClass('d-none');
2350 getChildContainer : function()
2356 return this.el.select('.roo-card-body-ctr',true).first();
2359 initEvents: function()
2361 this.bodyEl = this.el.select('.card-body',true).first();
2362 this.containerEl = this.getChildContainer();
2364 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2365 containerScroll: true,
2366 ddGroup: this.drag_group || 'default_card_drag_group'
2368 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2370 if (this.dropable) {
2371 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2372 containerScroll: true,
2373 ddGroup: this.drop_group || 'default_card_drag_group'
2375 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2376 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2377 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2378 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2379 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2382 if (this.collapsable) {
2383 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2385 if (this.rotateable) {
2386 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2388 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2390 this.footerEl = this.el.select('.card-footer',true).first();
2391 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2392 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2393 this.headerEl = this.el.select('.card-header',true).first();
2396 this.el.addClass('roo-card-rotated');
2397 this.fireEvent('rotate', this, true);
2399 this.header_imageEl = this.el.select('.card-img-top',true).first();
2400 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2403 getDragData : function(e)
2405 var target = this.getEl();
2407 //this.handleSelection(e);
2412 nodes: this.getEl(),
2417 dragData.ddel = target.dom ; // the div element
2418 Roo.log(target.getWidth( ));
2419 dragData.ddel.style.width = target.getWidth() + 'px';
2426 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2427 * whole Element becomes the target, and this causes the drop gesture to append.
2429 * Returns an object:
2432 position : 'below' or 'above'
2433 card : relateive to card OBJECT (or true for no cards listed)
2434 items_n : relative to nth item in list
2435 card_n : relative to nth card in list
2440 getTargetFromEvent : function(e, dragged_card_el)
2442 var target = e.getTarget();
2443 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2444 target = target.parentNode;
2455 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2456 // see if target is one of the 'cards'...
2459 //Roo.log(this.items.length);
2462 var last_card_n = 0;
2464 for (var i = 0;i< this.items.length;i++) {
2466 if (!this.items[i].el.hasClass('card')) {
2469 pos = this.getDropPoint(e, this.items[i].el.dom);
2471 cards_len = ret.cards.length;
2472 //Roo.log(this.items[i].el.dom.id);
2473 ret.cards.push(this.items[i]);
2475 if (ret.card_n < 0 && pos == 'above') {
2476 ret.position = cards_len > 0 ? 'below' : pos;
2477 ret.items_n = i > 0 ? i - 1 : 0;
2478 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2479 ret.card = ret.cards[ret.card_n];
2482 if (!ret.cards.length) {
2484 ret.position = 'below';
2488 // could not find a card.. stick it at the end..
2489 if (ret.card_n < 0) {
2490 ret.card_n = last_card_n;
2491 ret.card = ret.cards[last_card_n];
2492 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2493 ret.position = 'below';
2496 if (this.items[ret.items_n].el == dragged_card_el) {
2500 if (ret.position == 'below') {
2501 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2503 if (card_after && card_after.el == dragged_card_el) {
2510 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2512 if (card_before && card_before.el == dragged_card_el) {
2519 onNodeEnter : function(n, dd, e, data){
2522 onNodeOver : function(n, dd, e, data)
2525 var target_info = this.getTargetFromEvent(e,data.source.el);
2526 if (target_info === false) {
2527 this.dropPlaceHolder('hide');
2530 Roo.log(['getTargetFromEvent', target_info ]);
2533 if (this.fireEvent('cardover', this, [ data ]) === false) {
2537 this.dropPlaceHolder('show', target_info,data);
2541 onNodeOut : function(n, dd, e, data){
2542 this.dropPlaceHolder('hide');
2545 onNodeDrop : function(n, dd, e, data)
2548 // call drop - return false if
2550 // this could actually fail - if the Network drops..
2551 // we will ignore this at present..- client should probably reload
2552 // the whole set of cards if stuff like that fails.
2555 var info = this.getTargetFromEvent(e,data.source.el);
2556 if (info === false) {
2559 this.dropPlaceHolder('hide');
2563 this.acceptCard(data.source, info.position, info.card, info.items_n);
2567 firstChildCard : function()
2569 for (var i = 0;i< this.items.length;i++) {
2571 if (!this.items[i].el.hasClass('card')) {
2574 return this.items[i];
2576 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2581 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2583 acceptCard : function(move_card, position, next_to_card )
2585 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2589 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2591 move_card.parent().removeCard(move_card);
2594 var dom = move_card.el.dom;
2595 dom.style.width = ''; // clear with - which is set by drag.
2597 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2598 var cardel = next_to_card.el.dom;
2600 if (position == 'above' ) {
2601 cardel.parentNode.insertBefore(dom, cardel);
2602 } else if (cardel.nextSibling) {
2603 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2605 cardel.parentNode.append(dom);
2608 // card container???
2609 this.containerEl.dom.append(dom);
2612 //FIXME HANDLE card = true
2614 // add this to the correct place in items.
2616 // remove Card from items.
2619 if (this.items.length) {
2621 //Roo.log([info.items_n, info.position, this.items.length]);
2622 for (var i =0; i < this.items.length; i++) {
2623 if (i == to_items_n && position == 'above') {
2624 nitems.push(move_card);
2626 nitems.push(this.items[i]);
2627 if (i == to_items_n && position == 'below') {
2628 nitems.push(move_card);
2631 this.items = nitems;
2632 Roo.log(this.items);
2634 this.items.push(move_card);
2637 move_card.parentId = this.id;
2643 removeCard : function(c)
2645 this.items = this.items.filter(function(e) { return e != c });
2648 dom.parentNode.removeChild(dom);
2649 dom.style.width = ''; // clear with - which is set by drag.
2654 /** Decide whether to drop above or below a View node. */
2655 getDropPoint : function(e, n, dd)
2660 if (n == this.containerEl.dom) {
2663 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2664 var c = t + (b - t) / 2;
2665 var y = Roo.lib.Event.getPageY(e);
2672 onToggleCollapse : function(e)
2674 if (this.collapsed) {
2675 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2676 this.collapsableEl.addClass('show');
2677 this.collapsed = false;
2680 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2681 this.collapsableEl.removeClass('show');
2682 this.collapsed = true;
2687 onToggleRotate : function(e)
2689 this.collapsableEl.removeClass('show');
2690 this.footerEl.removeClass('d-none');
2691 this.el.removeClass('roo-card-rotated');
2692 this.el.removeClass('d-none');
2695 this.collapsableEl.addClass('show');
2696 this.rotated = false;
2697 this.fireEvent('rotate', this, this.rotated);
2700 this.el.addClass('roo-card-rotated');
2701 this.footerEl.addClass('d-none');
2702 this.el.select('.roo-collapsable').removeClass('show');
2704 this.rotated = true;
2705 this.fireEvent('rotate', this, this.rotated);
2709 dropPlaceHolder: function (action, info, data)
2711 if (this.dropEl === false) {
2712 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2716 this.dropEl.removeClass(['d-none', 'd-block']);
2717 if (action == 'hide') {
2719 this.dropEl.addClass('d-none');
2722 // FIXME - info.card == true!!!
2723 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2725 if (info.card !== true) {
2726 var cardel = info.card.el.dom;
2728 if (info.position == 'above') {
2729 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2730 } else if (cardel.nextSibling) {
2731 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2733 cardel.parentNode.append(this.dropEl.dom);
2736 // card container???
2737 this.containerEl.dom.append(this.dropEl.dom);
2740 this.dropEl.addClass('d-block roo-card-dropzone');
2742 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2749 setHeaderText: function(html)
2752 if (this.headerContainerEl) {
2753 this.headerContainerEl.dom.innerHTML = html;
2756 onHeaderImageLoad : function(ev, he)
2758 if (!this.header_image_fit_square) {
2762 var hw = he.naturalHeight / he.naturalWidth;
2765 //var w = he.dom.naturalWidth;
2768 he.style.position = 'relative';
2770 var nw = (ww * (1/hw));
2771 Roo.get(he).setSize( ww * (1/hw), ww);
2772 he.style.left = ((ww - nw)/ 2) + 'px';
2773 he.style.position = 'relative';
2784 * Card header - holder for the card header elements.
2789 * @class Roo.bootstrap.CardHeader
2790 * @extends Roo.bootstrap.Element
2791 * @parent Roo.bootstrap.Card
2792 * @children Roo.bootstrap.Component
2793 * Bootstrap CardHeader class
2795 * Create a new Card Header - that you can embed children into
2796 * @param {Object} config The config object
2799 Roo.bootstrap.CardHeader = function(config){
2800 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2803 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2806 container_method : 'getCardHeader'
2819 * Card footer - holder for the card footer elements.
2824 * @class Roo.bootstrap.CardFooter
2825 * @extends Roo.bootstrap.Element
2826 * @parent Roo.bootstrap.Card
2827 * @children Roo.bootstrap.Component
2828 * Bootstrap CardFooter class
2831 * Create a new Card Footer - that you can embed children into
2832 * @param {Object} config The config object
2835 Roo.bootstrap.CardFooter = function(config){
2836 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2839 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2842 container_method : 'getCardFooter'
2855 * Card header - holder for the card header elements.
2860 * @class Roo.bootstrap.CardImageTop
2861 * @extends Roo.bootstrap.Element
2862 * @parent Roo.bootstrap.Card
2863 * @children Roo.bootstrap.Component
2864 * Bootstrap CardImageTop class
2867 * Create a new Card Image Top container
2868 * @param {Object} config The config object
2871 Roo.bootstrap.CardImageTop = function(config){
2872 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2875 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2878 container_method : 'getCardImageTop'
2893 * @class Roo.bootstrap.ButtonUploader
2894 * @extends Roo.bootstrap.Button
2895 * Bootstrap Button Uploader class - it's a button which when you add files to it
2898 * @cfg {Number} errorTimeout default 3000
2899 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2900 * @cfg {Array} html The button text.
2901 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2904 * Create a new CardUploader
2905 * @param {Object} config The config object
2908 Roo.bootstrap.ButtonUploader = function(config){
2912 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2918 * @event beforeselect
2919 * When button is pressed, before show upload files dialog is shown
2920 * @param {Roo.bootstrap.UploaderButton} this
2923 'beforeselect' : true,
2925 * @event fired when files have been selected,
2926 * When a the download link is clicked
2927 * @param {Roo.bootstrap.UploaderButton} this
2928 * @param {Array} Array of files that have been uploaded
2935 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2938 errorTimeout : 3000,
2942 fileCollection : false,
2947 getAutoCreate : function()
2954 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2962 initEvents : function()
2965 Roo.bootstrap.Button.prototype.initEvents.call(this);
2971 this.urlAPI = (window.createObjectURL && window) ||
2972 (window.URL && URL.revokeObjectURL && URL) ||
2973 (window.webkitURL && webkitURL);
2978 cls : 'd-none roo-card-upload-selector'
2981 if (this.multiple) {
2982 im.multiple = 'multiple';
2984 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2986 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2988 this.selectorEl.on('change', this.onFileSelected, this);
2995 onClick : function(e)
2999 if ( this.fireEvent('beforeselect', this) === false) {
3003 this.selectorEl.dom.click();
3007 onFileSelected : function(e)
3011 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3014 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3015 this.selectorEl.dom.value = '';// hopefully reset..
3017 this.fireEvent('uploaded', this, files );
3025 * addCard - add an Attachment to the uploader
3026 * @param data - the data about the image to upload
3030 title : "Title of file",
3031 is_uploaded : false,
3032 src : "http://.....",
3033 srcfile : { the File upload object },
3034 mimetype : file.type,
3037 .. any other data...
3062 * @class Roo.bootstrap.Img
3063 * @extends Roo.bootstrap.Component
3064 * Bootstrap Img class
3065 * @cfg {Boolean} imgResponsive false | true
3066 * @cfg {String} border rounded | circle | thumbnail
3067 * @cfg {String} src image source
3068 * @cfg {String} alt image alternative text
3069 * @cfg {String} href a tag href
3070 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3071 * @cfg {String} xsUrl xs image source
3072 * @cfg {String} smUrl sm image source
3073 * @cfg {String} mdUrl md image source
3074 * @cfg {String} lgUrl lg image source
3075 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3078 * Create a new Input
3079 * @param {Object} config The config object
3082 Roo.bootstrap.Img = function(config){
3083 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3089 * The img click event for the img.
3090 * @param {Roo.EventObject} e
3095 * The when any image loads
3096 * @param {Roo.EventObject} e
3102 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3104 imgResponsive: true,
3113 backgroundContain : false,
3115 getAutoCreate : function()
3117 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3118 return this.createSingleImg();
3123 cls: 'roo-image-responsive-group',
3128 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3130 if(!_this[size + 'Url']){
3136 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3137 html: _this.html || cfg.html,
3138 src: _this[size + 'Url']
3141 img.cls += ' roo-image-responsive-' + size;
3143 var s = ['xs', 'sm', 'md', 'lg'];
3145 s.splice(s.indexOf(size), 1);
3147 Roo.each(s, function(ss){
3148 img.cls += ' hidden-' + ss;
3151 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3152 cfg.cls += ' img-' + _this.border;
3156 cfg.alt = _this.alt;
3169 a.target = _this.target;
3173 cfg.cn.push((_this.href) ? a : img);
3180 createSingleImg : function()
3184 cls: (this.imgResponsive) ? 'img-responsive' : '',
3186 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3189 if (this.backgroundContain) {
3190 cfg.cls += ' background-contain';
3193 cfg.html = this.html || cfg.html;
3195 if (this.backgroundContain) {
3196 cfg.style="background-image: url(" + this.src + ')';
3198 cfg.src = this.src || cfg.src;
3201 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3202 cfg.cls += ' img-' + this.border;
3219 a.target = this.target;
3224 return (this.href) ? a : cfg;
3227 initEvents: function()
3230 this.el.on('click', this.onClick, this);
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 this.el.on('load', this.onImageLoad, this);
3235 // not sure if this works.. not tested
3236 this.el.select('img', true).on('load', this.onImageLoad, this);
3241 onClick : function(e)
3243 Roo.log('img onclick');
3244 this.fireEvent('click', this, e);
3246 onImageLoad: function(e)
3248 Roo.log('img load');
3249 this.fireEvent('load', this, e);
3253 * Sets the url of the image - used to update it
3254 * @param {String} url the url of the image
3257 setSrc : function(url)
3261 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3262 if (this.backgroundContain) {
3263 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3265 this.el.dom.src = url;
3270 this.el.select('img', true).first().dom.src = url;
3286 * @class Roo.bootstrap.Link
3287 * @extends Roo.bootstrap.Component
3288 * @children Roo.bootstrap.Component
3289 * Bootstrap Link Class (eg. '<a href>')
3291 * @cfg {String} alt image alternative text
3292 * @cfg {String} href a tag href
3293 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3294 * @cfg {String} html the content of the link.
3295 * @cfg {String} anchor name for the anchor link
3296 * @cfg {String} fa - favicon
3298 * @cfg {Boolean} preventDefault (true | false) default false
3302 * Create a new Input
3303 * @param {Object} config The config object
3306 Roo.bootstrap.Link = function(config){
3307 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3313 * The img click event for the img.
3314 * @param {Roo.EventObject} e
3320 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3324 preventDefault: false,
3330 getAutoCreate : function()
3332 var html = this.html || '';
3334 if (this.fa !== false) {
3335 html = '<i class="fa fa-' + this.fa + '"></i>';
3340 // anchor's do not require html/href...
3341 if (this.anchor === false) {
3343 cfg.href = this.href || '#';
3345 cfg.name = this.anchor;
3346 if (this.html !== false || this.fa !== false) {
3349 if (this.href !== false) {
3350 cfg.href = this.href;
3354 if(this.alt !== false){
3359 if(this.target !== false) {
3360 cfg.target = this.target;
3366 initEvents: function() {
3368 if(!this.href || this.preventDefault){
3369 this.el.on('click', this.onClick, this);
3373 onClick : function(e)
3375 if(this.preventDefault){
3378 //Roo.log('img onclick');
3379 this.fireEvent('click', this, e);
3392 * @class Roo.bootstrap.Header
3393 * @extends Roo.bootstrap.Component
3394 * @children Roo.bootstrap.Component
3395 * Bootstrap Header class
3398 * @cfg {String} html content of header
3399 * @cfg {Number} level (1|2|3|4|5|6) default 1
3402 * Create a new Header
3403 * @param {Object} config The config object
3407 Roo.bootstrap.Header = function(config){
3408 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3411 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3419 getAutoCreate : function(){
3424 tag: 'h' + (1 *this.level),
3425 html: this.html || ''
3436 * @class Roo.bootstrap.MenuMgr
3438 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3441 Roo.bootstrap.menu.Manager = function(){
3442 var menus, active, groups = {}, attached = false, lastShow = new Date();
3444 // private - called when first menu is created
3447 active = new Roo.util.MixedCollection();
3448 Roo.get(document).addKeyListener(27, function(){
3449 if(active.length > 0){
3457 if(active && active.length > 0){
3458 var c = active.clone();
3468 if(active.length < 1){
3469 Roo.get(document).un("mouseup", onMouseDown);
3477 var last = active.last();
3478 lastShow = new Date();
3481 Roo.get(document).on("mouseup", onMouseDown);
3486 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3487 m.parentMenu.activeChild = m;
3488 }else if(last && last.isVisible()){
3489 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3494 function onBeforeHide(m){
3496 m.activeChild.hide();
3498 if(m.autoHideTimer){
3499 clearTimeout(m.autoHideTimer);
3500 delete m.autoHideTimer;
3505 function onBeforeShow(m){
3506 var pm = m.parentMenu;
3507 if(!pm && !m.allowOtherMenus){
3509 }else if(pm && pm.activeChild && active != m){
3510 pm.activeChild.hide();
3514 // private this should really trigger on mouseup..
3515 function onMouseDown(e){
3516 Roo.log("on Mouse Up");
3518 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3519 Roo.log("MenuManager hideAll");
3528 function onBeforeCheck(mi, state){
3530 var g = groups[mi.group];
3531 for(var i = 0, l = g.length; i < l; i++){
3533 g[i].setChecked(false);
3542 * Hides all menus that are currently visible
3544 hideAll : function(){
3549 register : function(menu){
3553 menus[menu.id] = menu;
3554 menu.on("beforehide", onBeforeHide);
3555 menu.on("hide", onHide);
3556 menu.on("beforeshow", onBeforeShow);
3557 menu.on("show", onShow);
3559 if(g && menu.events["checkchange"]){
3563 groups[g].push(menu);
3564 menu.on("checkchange", onCheck);
3569 * Returns a {@link Roo.menu.Menu} object
3570 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3571 * be used to generate and return a new Menu instance.
3573 get : function(menu){
3574 if(typeof menu == "string"){ // menu id
3576 }else if(menu.events){ // menu instance
3579 /*else if(typeof menu.length == 'number'){ // array of menu items?
3580 return new Roo.bootstrap.Menu({items:menu});
3581 }else{ // otherwise, must be a config
3582 return new Roo.bootstrap.Menu(menu);
3589 unregister : function(menu){
3590 delete menus[menu.id];
3591 menu.un("beforehide", onBeforeHide);
3592 menu.un("hide", onHide);
3593 menu.un("beforeshow", onBeforeShow);
3594 menu.un("show", onShow);
3596 if(g && menu.events["checkchange"]){
3597 groups[g].remove(menu);
3598 menu.un("checkchange", onCheck);
3603 registerCheckable : function(menuItem){
3604 var g = menuItem.group;
3609 groups[g].push(menuItem);
3610 menuItem.on("beforecheckchange", onBeforeCheck);
3615 unregisterCheckable : function(menuItem){
3616 var g = menuItem.group;
3618 groups[g].remove(menuItem);
3619 menuItem.un("beforecheckchange", onBeforeCheck);
3625 * @class Roo.bootstrap.menu.Menu
3626 * @extends Roo.bootstrap.Component
3628 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3630 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3632 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3633 * @cfg {bool} hidden if the menu should be hidden when rendered.
3634 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3635 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3636 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3637 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3641 * @param {Object} config The config objectQ
3645 Roo.bootstrap.menu.Menu = function(config){
3647 if (config.type == 'treeview') {
3648 // normally menu's are drawn attached to the document to handle layering etc..
3649 // however treeview (used by the docs menu is drawn into the parent element)
3650 this.container_method = 'getChildContainer';
3653 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3654 if (this.registerMenu && this.type != 'treeview') {
3655 Roo.bootstrap.menu.Manager.register(this);
3662 * Fires before this menu is displayed (return false to block)
3663 * @param {Roo.menu.Menu} this
3668 * Fires before this menu is hidden (return false to block)
3669 * @param {Roo.menu.Menu} this
3674 * Fires after this menu is displayed
3675 * @param {Roo.menu.Menu} this
3680 * Fires after this menu is hidden
3681 * @param {Roo.menu.Menu} this
3686 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3687 * @param {Roo.menu.Menu} this
3688 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3689 * @param {Roo.EventObject} e
3694 * Fires when the mouse is hovering over this menu
3695 * @param {Roo.menu.Menu} this
3696 * @param {Roo.EventObject} e
3697 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3702 * Fires when the mouse exits this menu
3703 * @param {Roo.menu.Menu} this
3704 * @param {Roo.EventObject} e
3705 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3710 * Fires when a menu item contained in this menu is clicked
3711 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3712 * @param {Roo.EventObject} e
3716 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3719 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3723 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3726 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3728 registerMenu : true,
3730 menuItems :false, // stores the menu items..
3740 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3742 hideTrigger : false,
3747 getChildContainer : function() {
3751 getAutoCreate : function(){
3753 //if (['right'].indexOf(this.align)!==-1) {
3754 // cfg.cn[1].cls += ' pull-right'
3759 cls : 'dropdown-menu shadow' ,
3760 style : 'z-index:1000'
3764 if (this.type === 'submenu') {
3765 cfg.cls = 'submenu active';
3767 if (this.type === 'treeview') {
3768 cfg.cls = 'treeview-menu';
3773 initEvents : function() {
3775 // Roo.log("ADD event");
3776 // Roo.log(this.triggerEl.dom);
3777 if (this.triggerEl) {
3779 this.triggerEl.on('click', this.onTriggerClick, this);
3781 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3783 if (!this.hideTrigger) {
3784 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3785 // dropdown toggle on the 'a' in BS4?
3786 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3788 this.triggerEl.addClass('dropdown-toggle');
3794 this.el.on('touchstart' , this.onTouch, this);
3796 this.el.on('click' , this.onClick, this);
3798 this.el.on("mouseover", this.onMouseOver, this);
3799 this.el.on("mouseout", this.onMouseOut, this);
3803 findTargetItem : function(e)
3805 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3809 //Roo.log(t); Roo.log(t.id);
3811 //Roo.log(this.menuitems);
3812 return this.menuitems.get(t.id);
3814 //return this.items.get(t.menuItemId);
3820 onTouch : function(e)
3822 Roo.log("menu.onTouch");
3823 //e.stopEvent(); this make the user popdown broken
3827 onClick : function(e)
3829 Roo.log("menu.onClick");
3831 var t = this.findTargetItem(e);
3832 if(!t || t.isContainer){
3837 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3838 if(t == this.activeItem && t.shouldDeactivate(e)){
3839 this.activeItem.deactivate();
3840 delete this.activeItem;
3844 this.setActiveItem(t, true);
3852 Roo.log('pass click event');
3856 this.fireEvent("click", this, t, e);
3860 if(!t.href.length || t.href == '#'){
3861 (function() { _this.hide(); }).defer(100);
3866 onMouseOver : function(e){
3867 var t = this.findTargetItem(e);
3870 // if(t.canActivate && !t.disabled){
3871 // this.setActiveItem(t, true);
3875 this.fireEvent("mouseover", this, e, t);
3877 isVisible : function(){
3878 return !this.hidden;
3880 onMouseOut : function(e){
3881 var t = this.findTargetItem(e);
3884 // if(t == this.activeItem && t.shouldDeactivate(e)){
3885 // this.activeItem.deactivate();
3886 // delete this.activeItem;
3889 this.fireEvent("mouseout", this, e, t);
3894 * Displays this menu relative to another element
3895 * @param {String/HTMLElement/Roo.Element} element The element to align to
3896 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3897 * the element (defaults to this.defaultAlign)
3898 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3900 show : function(el, pos, parentMenu)
3902 if (false === this.fireEvent("beforeshow", this)) {
3903 Roo.log("show canceled");
3906 this.parentMenu = parentMenu;
3910 this.el.addClass('show'); // show otherwise we do not know how big we are..
3912 var xy = this.el.getAlignToXY(el, pos);
3914 // bl-tl << left align below
3915 // tl-bl << left align
3917 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3918 // if it goes to far to the right.. -> align left.
3919 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3922 // was left align - go right?
3923 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3926 // goes down the bottom
3927 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3929 var a = this.align.replace('?', '').split('-');
3930 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3934 this.showAt( xy , parentMenu, false);
3937 * Displays this menu at a specific xy position
3938 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3939 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3941 showAt : function(xy, parentMenu, /* private: */_e){
3942 this.parentMenu = parentMenu;
3947 this.fireEvent("beforeshow", this);
3948 //xy = this.el.adjustForConstraints(xy);
3952 this.hideMenuItems();
3953 this.hidden = false;
3954 if (this.triggerEl) {
3955 this.triggerEl.addClass('open');
3958 this.el.addClass('show');
3962 // reassign x when hitting right
3964 // reassign y when hitting bottom
3966 // but the list may align on trigger left or trigger top... should it be a properity?
3968 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3973 this.fireEvent("show", this);
3979 this.doFocus.defer(50, this);
3983 doFocus : function(){
3985 this.focusEl.focus();
3990 * Hides this menu and optionally all parent menus
3991 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3993 hide : function(deep)
3995 if (false === this.fireEvent("beforehide", this)) {
3996 Roo.log("hide canceled");
3999 this.hideMenuItems();
4000 if(this.el && this.isVisible()){
4002 if(this.activeItem){
4003 this.activeItem.deactivate();
4004 this.activeItem = null;
4006 if (this.triggerEl) {
4007 this.triggerEl.removeClass('open');
4010 this.el.removeClass('show');
4012 this.fireEvent("hide", this);
4014 if(deep === true && this.parentMenu){
4015 this.parentMenu.hide(true);
4019 onTriggerClick : function(e)
4021 Roo.log('trigger click');
4023 var target = e.getTarget();
4025 Roo.log(target.nodeName.toLowerCase());
4027 if(target.nodeName.toLowerCase() === 'i'){
4033 onTriggerPress : function(e)
4035 Roo.log('trigger press');
4036 //Roo.log(e.getTarget());
4037 // Roo.log(this.triggerEl.dom);
4039 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4040 var pel = Roo.get(e.getTarget());
4041 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4042 Roo.log('is treeview or dropdown?');
4046 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4050 if (this.isVisible()) {
4056 this.show(this.triggerEl, this.align, false);
4059 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4066 hideMenuItems : function()
4068 Roo.log("hide Menu Items");
4073 this.el.select('.open',true).each(function(aa) {
4075 aa.removeClass('open');
4079 addxtypeChild : function (tree, cntr) {
4080 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4082 this.menuitems.add(comp);
4094 this.getEl().dom.innerHTML = '';
4095 this.menuitems.clear();
4101 * @class Roo.bootstrap.menu.Item
4102 * @extends Roo.bootstrap.Component
4103 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4104 * @parent Roo.bootstrap.menu.Menu
4106 * Bootstrap MenuItem class
4108 * @cfg {String} html the menu label
4109 * @cfg {String} href the link
4110 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4111 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4112 * @cfg {Boolean} active used on sidebars to highlight active itesm
4113 * @cfg {String} fa favicon to show on left of menu item.
4114 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4118 * Create a new MenuItem
4119 * @param {Object} config The config object
4123 Roo.bootstrap.menu.Item = function(config){
4124 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4129 * The raw click event for the entire grid.
4130 * @param {Roo.bootstrap.menu.Item} this
4131 * @param {Roo.EventObject} e
4137 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4141 preventDefault: false,
4142 isContainer : false,
4146 getAutoCreate : function(){
4148 if(this.isContainer){
4151 cls: 'dropdown-menu-item '
4161 cls : 'dropdown-item',
4166 if (this.fa !== false) {
4169 cls : 'fa fa-' + this.fa
4178 cls: 'dropdown-menu-item',
4181 if (this.parent().type == 'treeview') {
4182 cfg.cls = 'treeview-menu';
4185 cfg.cls += ' active';
4190 anc.href = this.href || cfg.cn[0].href ;
4191 ctag.html = this.html || cfg.cn[0].html ;
4195 initEvents: function()
4197 if (this.parent().type == 'treeview') {
4198 this.el.select('a').on('click', this.onClick, this);
4202 this.menu.parentType = this.xtype;
4203 this.menu.triggerEl = this.el;
4204 this.menu = this.addxtype(Roo.apply({}, this.menu));
4208 onClick : function(e)
4210 //Roo.log('item on click ');
4212 if(this.href === false || this.preventDefault){
4215 //this.parent().hideMenuItems();
4217 this.fireEvent('click', this, e);
4231 * @class Roo.bootstrap.menu.Separator
4232 * @extends Roo.bootstrap.Component
4234 * @parent Roo.bootstrap.menu.Menu
4235 * Bootstrap Separator class
4238 * Create a new Separator
4239 * @param {Object} config The config object
4243 Roo.bootstrap.menu.Separator = function(config){
4244 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4247 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4249 getAutoCreate : function(){
4252 cls: 'dropdown-divider divider'
4268 * @class Roo.bootstrap.Modal
4269 * @extends Roo.bootstrap.Component
4270 * @parent none builder
4271 * @children Roo.bootstrap.Component
4272 * Bootstrap Modal class
4273 * @cfg {String} title Title of dialog
4274 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4275 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4276 * @cfg {Boolean} specificTitle default false
4277 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4278 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4279 * @cfg {Boolean} animate default true
4280 * @cfg {Boolean} allow_close default true
4281 * @cfg {Boolean} fitwindow default false
4282 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4283 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4284 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4285 * @cfg {String} size (sm|lg|xl) default empty
4286 * @cfg {Number} max_width set the max width of modal
4287 * @cfg {Boolean} editableTitle can the title be edited
4292 * Create a new Modal Dialog
4293 * @param {Object} config The config object
4296 Roo.bootstrap.Modal = function(config){
4297 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4302 * The raw btnclick event for the button
4303 * @param {Roo.EventObject} e
4308 * Fire when dialog resize
4309 * @param {Roo.bootstrap.Modal} this
4310 * @param {Roo.EventObject} e
4314 * @event titlechanged
4315 * Fire when the editable title has been changed
4316 * @param {Roo.bootstrap.Modal} this
4317 * @param {Roo.EventObject} value
4319 "titlechanged" : true
4322 this.buttons = this.buttons || [];
4325 this.tmpl = Roo.factory(this.tmpl);
4330 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4332 title : 'test dialog',
4342 specificTitle: false,
4344 buttonPosition: 'right',
4366 editableTitle : false,
4368 onRender : function(ct, position)
4370 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4373 var cfg = Roo.apply({}, this.getAutoCreate());
4376 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4378 //if (!cfg.name.length) {
4382 cfg.cls += ' ' + this.cls;
4385 cfg.style = this.style;
4387 this.el = Roo.get(document.body).createChild(cfg, position);
4389 //var type = this.el.dom.type;
4392 if(this.tabIndex !== undefined){
4393 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4396 this.dialogEl = this.el.select('.modal-dialog',true).first();
4397 this.bodyEl = this.el.select('.modal-body',true).first();
4398 this.closeEl = this.el.select('.modal-header .close', true).first();
4399 this.headerEl = this.el.select('.modal-header',true).first();
4400 this.titleEl = this.el.select('.modal-title',true).first();
4401 this.footerEl = this.el.select('.modal-footer',true).first();
4403 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4405 //this.el.addClass("x-dlg-modal");
4407 if (this.buttons.length) {
4408 Roo.each(this.buttons, function(bb) {
4409 var b = Roo.apply({}, bb);
4410 b.xns = b.xns || Roo.bootstrap;
4411 b.xtype = b.xtype || 'Button';
4412 if (typeof(b.listeners) == 'undefined') {
4413 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4416 var btn = Roo.factory(b);
4418 btn.render(this.getButtonContainer());
4422 // render the children.
4425 if(typeof(this.items) != 'undefined'){
4426 var items = this.items;
4429 for(var i =0;i < items.length;i++) {
4430 // we force children not to montor widnow resize - as we do that for them.
4431 items[i].monitorWindowResize = false;
4432 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4436 this.items = nitems;
4438 // where are these used - they used to be body/close/footer
4442 //this.el.addClass([this.fieldClass, this.cls]);
4446 getAutoCreate : function()
4448 // we will default to modal-body-overflow - might need to remove or make optional later.
4450 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4451 html : this.html || ''
4456 cls : 'modal-title',
4460 if(this.specificTitle){ // WTF is this?
4465 if (this.allow_close && Roo.bootstrap.version == 3) {
4475 if (this.editableTitle) {
4477 cls: 'form-control roo-editable-title d-none',
4483 if (this.allow_close && Roo.bootstrap.version == 4) {
4493 if(this.size.length){
4494 size = 'modal-' + this.size;
4497 var footer = Roo.bootstrap.version == 3 ?
4499 cls : 'modal-footer',
4503 cls: 'btn-' + this.buttonPosition
4508 { // BS4 uses mr-auto on left buttons....
4509 cls : 'modal-footer'
4520 cls: "modal-dialog " + size,
4523 cls : "modal-content",
4526 cls : 'modal-header',
4541 modal.cls += ' fade';
4547 getChildContainer : function() {
4552 getButtonContainer : function() {
4554 return Roo.bootstrap.version == 4 ?
4555 this.el.select('.modal-footer',true).first()
4556 : this.el.select('.modal-footer div',true).first();
4560 closeClick : function()
4565 initEvents : function()
4567 if (this.allow_close) {
4568 this.closeEl.on('click', this.closeClick, this);
4570 Roo.EventManager.onWindowResize(this.resize, this, true);
4571 if (this.editableTitle) {
4572 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4573 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4574 this.headerEditEl.on('keyup', function(e) {
4575 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4576 this.toggleHeaderInput(false)
4579 this.headerEditEl.on('blur', function(e) {
4580 this.toggleHeaderInput(false)
4589 this.maskEl.setSize(
4590 Roo.lib.Dom.getViewWidth(true),
4591 Roo.lib.Dom.getViewHeight(true)
4594 if (this.fitwindow) {
4596 this.dialogEl.setStyle( { 'max-width' : '100%' });
4598 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4599 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4604 if(this.max_width !== 0) {
4606 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4609 this.setSize(w, this.height);
4613 if(this.max_height) {
4614 this.setSize(w,Math.min(
4616 Roo.lib.Dom.getViewportHeight(true) - 60
4622 if(!this.fit_content) {
4623 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4627 this.setSize(w, Math.min(
4629 this.headerEl.getHeight() +
4630 this.footerEl.getHeight() +
4631 this.getChildHeight(this.bodyEl.dom.childNodes),
4632 Roo.lib.Dom.getViewportHeight(true) - 60)
4638 setSize : function(w,h)
4645 // any layout/border etc.. resize..
4647 this.items.forEach( function(e) {
4648 e.layout ? e.layout() : false;
4657 if (!this.rendered) {
4660 this.toggleHeaderInput(false);
4661 //this.el.setStyle('display', 'block');
4662 this.el.removeClass('hideing');
4663 this.el.dom.style.display='block';
4665 Roo.get(document.body).addClass('modal-open');
4667 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4670 this.el.addClass('show');
4671 this.el.addClass('in');
4674 this.el.addClass('show');
4675 this.el.addClass('in');
4678 // not sure how we can show data in here..
4680 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4683 Roo.get(document.body).addClass("x-body-masked");
4685 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4686 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4687 this.maskEl.dom.style.display = 'block';
4688 this.maskEl.addClass('show');
4693 this.fireEvent('show', this);
4695 // set zindex here - otherwise it appears to be ignored...
4696 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4699 // this is for children that are... layout.Border
4701 this.items.forEach( function(e) {
4702 e.layout ? e.layout() : false;
4710 if(this.fireEvent("beforehide", this) !== false){
4712 this.maskEl.removeClass('show');
4714 this.maskEl.dom.style.display = '';
4715 Roo.get(document.body).removeClass("x-body-masked");
4716 this.el.removeClass('in');
4717 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4719 if(this.animate){ // why
4720 this.el.addClass('hideing');
4721 this.el.removeClass('show');
4723 if (!this.el.hasClass('hideing')) {
4724 return; // it's been shown again...
4727 this.el.dom.style.display='';
4729 Roo.get(document.body).removeClass('modal-open');
4730 this.el.removeClass('hideing');
4734 this.el.removeClass('show');
4735 this.el.dom.style.display='';
4736 Roo.get(document.body).removeClass('modal-open');
4739 this.fireEvent('hide', this);
4742 isVisible : function()
4745 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4749 addButton : function(str, cb)
4753 var b = Roo.apply({}, { html : str } );
4754 b.xns = b.xns || Roo.bootstrap;
4755 b.xtype = b.xtype || 'Button';
4756 if (typeof(b.listeners) == 'undefined') {
4757 b.listeners = { click : cb.createDelegate(this) };
4760 var btn = Roo.factory(b);
4762 btn.render(this.getButtonContainer());
4768 setDefaultButton : function(btn)
4770 //this.el.select('.modal-footer').()
4773 resizeTo: function(w,h)
4775 this.dialogEl.setWidth(w);
4777 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4779 this.bodyEl.setHeight(h - diff);
4781 this.fireEvent('resize', this);
4784 setContentSize : function(w, h)
4788 onButtonClick: function(btn,e)
4791 this.fireEvent('btnclick', btn.name, e);
4794 * Set the title of the Dialog
4795 * @param {String} str new Title
4797 setTitle: function(str) {
4798 this.titleEl.dom.innerHTML = str;
4802 * Set the body of the Dialog
4803 * @param {String} str new Title
4805 setBody: function(str) {
4806 this.bodyEl.dom.innerHTML = str;
4809 * Set the body of the Dialog using the template
4810 * @param {Obj} data - apply this data to the template and replace the body contents.
4812 applyBody: function(obj)
4815 Roo.log("Error - using apply Body without a template");
4818 this.tmpl.overwrite(this.bodyEl, obj);
4821 getChildHeight : function(child_nodes)
4825 child_nodes.length == 0
4830 var child_height = 0;
4832 for(var i = 0; i < child_nodes.length; i++) {
4835 * for modal with tabs...
4836 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4838 var layout_childs = child_nodes[i].childNodes;
4840 for(var j = 0; j < layout_childs.length; j++) {
4842 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4844 var layout_body_childs = layout_childs[j].childNodes;
4846 for(var k = 0; k < layout_body_childs.length; k++) {
4848 if(layout_body_childs[k].classList.contains('navbar')) {
4849 child_height += layout_body_childs[k].offsetHeight;
4853 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4855 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4857 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4859 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4860 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4875 child_height += child_nodes[i].offsetHeight;
4876 // Roo.log(child_nodes[i].offsetHeight);
4879 return child_height;
4881 toggleHeaderInput : function(is_edit)
4883 if (!this.editableTitle) {
4884 return; // not editable.
4886 if (is_edit && this.is_header_editing) {
4887 return; // already editing..
4891 this.headerEditEl.dom.value = this.title;
4892 this.headerEditEl.removeClass('d-none');
4893 this.headerEditEl.dom.focus();
4894 this.titleEl.addClass('d-none');
4896 this.is_header_editing = true;
4899 // flip back to not editing.
4900 this.title = this.headerEditEl.dom.value;
4901 this.headerEditEl.addClass('d-none');
4902 this.titleEl.removeClass('d-none');
4903 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4904 this.is_header_editing = false;
4905 this.fireEvent('titlechanged', this, this.title);
4914 Roo.apply(Roo.bootstrap.Modal, {
4916 * Button config that displays a single OK button
4925 * Button config that displays Yes and No buttons
4941 * Button config that displays OK and Cancel buttons
4956 * Button config that displays Yes, No and Cancel buttons
4981 * messagebox - can be used as a replace
4985 * @class Roo.MessageBox
4986 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4990 Roo.Msg.alert('Status', 'Changes saved successfully.');
4992 // Prompt for user data:
4993 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4995 // process text value...
4999 // Show a dialog using config options:
5001 title:'Save Changes?',
5002 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
5003 buttons: Roo.Msg.YESNOCANCEL,
5010 Roo.bootstrap.MessageBox = function(){
5011 var dlg, opt, mask, waitTimer;
5012 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5013 var buttons, activeTextEl, bwidth;
5017 var handleButton = function(button){
5019 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5023 var handleHide = function(){
5025 dlg.el.removeClass(opt.cls);
5028 // Roo.TaskMgr.stop(waitTimer);
5029 // waitTimer = null;
5034 var updateButtons = function(b){
5037 buttons["ok"].hide();
5038 buttons["cancel"].hide();
5039 buttons["yes"].hide();
5040 buttons["no"].hide();
5041 dlg.footerEl.hide();
5045 dlg.footerEl.show();
5046 for(var k in buttons){
5047 if(typeof buttons[k] != "function"){
5050 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5051 width += buttons[k].el.getWidth()+15;
5061 var handleEsc = function(d, k, e){
5062 if(opt && opt.closable !== false){
5072 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5073 * @return {Roo.BasicDialog} The BasicDialog element
5075 getDialog : function(){
5077 dlg = new Roo.bootstrap.Modal( {
5080 //constraintoviewport:false,
5082 //collapsible : false,
5087 //buttonAlign:"center",
5088 closeClick : function(){
5089 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5092 handleButton("cancel");
5097 dlg.on("hide", handleHide);
5099 //dlg.addKeyListener(27, handleEsc);
5101 this.buttons = buttons;
5102 var bt = this.buttonText;
5103 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5104 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5105 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5106 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5108 bodyEl = dlg.bodyEl.createChild({
5110 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5111 '<textarea class="roo-mb-textarea"></textarea>' +
5112 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5114 msgEl = bodyEl.dom.firstChild;
5115 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5116 textboxEl.enableDisplayMode();
5117 textboxEl.addKeyListener([10,13], function(){
5118 if(dlg.isVisible() && opt && opt.buttons){
5121 }else if(opt.buttons.yes){
5122 handleButton("yes");
5126 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5127 textareaEl.enableDisplayMode();
5128 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5129 progressEl.enableDisplayMode();
5131 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5132 var pf = progressEl.dom.firstChild;
5134 pp = Roo.get(pf.firstChild);
5135 pp.setHeight(pf.offsetHeight);
5143 * Updates the message box body text
5144 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5145 * the XHTML-compliant non-breaking space character '&#160;')
5146 * @return {Roo.MessageBox} This message box
5148 updateText : function(text)
5150 if(!dlg.isVisible() && !opt.width){
5151 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5152 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5154 msgEl.innerHTML = text || ' ';
5156 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5157 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5159 Math.min(opt.width || cw , this.maxWidth),
5160 Math.max(opt.minWidth || this.minWidth, bwidth)
5163 activeTextEl.setWidth(w);
5165 if(dlg.isVisible()){
5166 dlg.fixedcenter = false;
5168 // to big, make it scroll. = But as usual stupid IE does not support
5171 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5172 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5173 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5175 bodyEl.dom.style.height = '';
5176 bodyEl.dom.style.overflowY = '';
5179 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5181 bodyEl.dom.style.overflowX = '';
5184 dlg.setContentSize(w, bodyEl.getHeight());
5185 if(dlg.isVisible()){
5186 dlg.fixedcenter = true;
5192 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5193 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5194 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5195 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5196 * @return {Roo.MessageBox} This message box
5198 updateProgress : function(value, text){
5200 this.updateText(text);
5203 if (pp) { // weird bug on my firefox - for some reason this is not defined
5204 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5205 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5211 * Returns true if the message box is currently displayed
5212 * @return {Boolean} True if the message box is visible, else false
5214 isVisible : function(){
5215 return dlg && dlg.isVisible();
5219 * Hides the message box if it is displayed
5222 if(this.isVisible()){
5228 * Displays a new message box, or reinitializes an existing message box, based on the config options
5229 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5230 * The following config object properties are supported:
5232 Property Type Description
5233 ---------- --------------- ------------------------------------------------------------------------------------
5234 animEl String/Element An id or Element from which the message box should animate as it opens and
5235 closes (defaults to undefined)
5236 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5237 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5238 closable Boolean False to hide the top-right close button (defaults to true). Note that
5239 progress and wait dialogs will ignore this property and always hide the
5240 close button as they can only be closed programmatically.
5241 cls String A custom CSS class to apply to the message box element
5242 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5243 displayed (defaults to 75)
5244 fn Function A callback function to execute after closing the dialog. The arguments to the
5245 function will be btn (the name of the button that was clicked, if applicable,
5246 e.g. "ok"), and text (the value of the active text field, if applicable).
5247 Progress and wait dialogs will ignore this option since they do not respond to
5248 user actions and can only be closed programmatically, so any required function
5249 should be called by the same code after it closes the dialog.
5250 icon String A CSS class that provides a background image to be used as an icon for
5251 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5252 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5253 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5254 modal Boolean False to allow user interaction with the page while the message box is
5255 displayed (defaults to true)
5256 msg String A string that will replace the existing message box body text (defaults
5257 to the XHTML-compliant non-breaking space character ' ')
5258 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5259 progress Boolean True to display a progress bar (defaults to false)
5260 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5261 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5262 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5263 title String The title text
5264 value String The string value to set into the active textbox element if displayed
5265 wait Boolean True to display a progress bar (defaults to false)
5266 width Number The width of the dialog in pixels
5273 msg: 'Please enter your address:',
5275 buttons: Roo.MessageBox.OKCANCEL,
5278 animEl: 'addAddressBtn'
5281 * @param {Object} config Configuration options
5282 * @return {Roo.MessageBox} This message box
5284 show : function(options)
5287 // this causes nightmares if you show one dialog after another
5288 // especially on callbacks..
5290 if(this.isVisible()){
5293 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5294 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5295 Roo.log("New Dialog Message:" + options.msg )
5296 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5297 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5300 var d = this.getDialog();
5302 d.setTitle(opt.title || " ");
5303 d.closeEl.setDisplayed(opt.closable !== false);
5304 activeTextEl = textboxEl;
5305 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5310 textareaEl.setHeight(typeof opt.multiline == "number" ?
5311 opt.multiline : this.defaultTextHeight);
5312 activeTextEl = textareaEl;
5321 progressEl.setDisplayed(opt.progress === true);
5323 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5325 this.updateProgress(0);
5326 activeTextEl.dom.value = opt.value || "";
5328 dlg.setDefaultButton(activeTextEl);
5330 var bs = opt.buttons;
5334 }else if(bs && bs.yes){
5335 db = buttons["yes"];
5337 dlg.setDefaultButton(db);
5339 bwidth = updateButtons(opt.buttons);
5340 this.updateText(opt.msg);
5342 d.el.addClass(opt.cls);
5344 d.proxyDrag = opt.proxyDrag === true;
5345 d.modal = opt.modal !== false;
5346 d.mask = opt.modal !== false ? mask : false;
5348 // force it to the end of the z-index stack so it gets a cursor in FF
5349 document.body.appendChild(dlg.el.dom);
5350 d.animateTarget = null;
5351 d.show(options.animEl);
5357 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5358 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5359 * and closing the message box when the process is complete.
5360 * @param {String} title The title bar text
5361 * @param {String} msg The message box body text
5362 * @return {Roo.MessageBox} This message box
5364 progress : function(title, msg){
5371 minWidth: this.minProgressWidth,
5378 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5379 * If a callback function is passed it will be called after the user clicks the button, and the
5380 * id of the button that was clicked will be passed as the only parameter to the callback
5381 * (could also be the top-right close button).
5382 * @param {String} title The title bar text
5383 * @param {String} msg The message box body text
5384 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5385 * @param {Object} scope (optional) The scope of the callback function
5386 * @return {Roo.MessageBox} This message box
5388 alert : function(title, msg, fn, scope)
5403 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5404 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5405 * You are responsible for closing the message box when the process is complete.
5406 * @param {String} msg The message box body text
5407 * @param {String} title (optional) The title bar text
5408 * @return {Roo.MessageBox} This message box
5410 wait : function(msg, title){
5421 waitTimer = Roo.TaskMgr.start({
5423 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5431 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5432 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5433 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5434 * @param {String} title The title bar text
5435 * @param {String} msg The message box body text
5436 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5437 * @param {Object} scope (optional) The scope of the callback function
5438 * @return {Roo.MessageBox} This message box
5440 confirm : function(title, msg, fn, scope){
5444 buttons: this.YESNO,
5453 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5454 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5455 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5456 * (could also be the top-right close button) and the text that was entered will be passed as the two
5457 * parameters to the callback.
5458 * @param {String} title The title bar text
5459 * @param {String} msg The message box body text
5460 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5461 * @param {Object} scope (optional) The scope of the callback function
5462 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5463 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5464 * @return {Roo.MessageBox} This message box
5466 prompt : function(title, msg, fn, scope, multiline){
5470 buttons: this.OKCANCEL,
5475 multiline: multiline,
5482 * Button config that displays a single OK button
5487 * Button config that displays Yes and No buttons
5490 YESNO : {yes:true, no:true},
5492 * Button config that displays OK and Cancel buttons
5495 OKCANCEL : {ok:true, cancel:true},
5497 * Button config that displays Yes, No and Cancel buttons
5500 YESNOCANCEL : {yes:true, no:true, cancel:true},
5503 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5506 defaultTextHeight : 75,
5508 * The maximum width in pixels of the message box (defaults to 600)
5513 * The minimum width in pixels of the message box (defaults to 100)
5518 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5519 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5522 minProgressWidth : 250,
5524 * An object containing the default button text strings that can be overriden for localized language support.
5525 * Supported properties are: ok, cancel, yes and no.
5526 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5539 * Shorthand for {@link Roo.MessageBox}
5541 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5542 Roo.Msg = Roo.Msg || Roo.MessageBox;
5551 * @class Roo.bootstrap.nav.Bar
5552 * @extends Roo.bootstrap.Component
5554 * Bootstrap Navbar class
5557 * Create a new Navbar
5558 * @param {Object} config The config object
5562 Roo.bootstrap.nav.Bar = function(config){
5563 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5567 * @event beforetoggle
5568 * Fire before toggle the menu
5569 * @param {Roo.EventObject} e
5571 "beforetoggle" : true
5575 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5584 getAutoCreate : function(){
5587 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5591 initEvents :function ()
5593 //Roo.log(this.el.select('.navbar-toggle',true));
5594 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5601 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5603 var size = this.el.getSize();
5604 this.maskEl.setSize(size.width, size.height);
5605 this.maskEl.enableDisplayMode("block");
5614 getChildContainer : function()
5616 if (this.el && this.el.select('.collapse').getCount()) {
5617 return this.el.select('.collapse',true).first();
5632 onToggle : function()
5635 if(this.fireEvent('beforetoggle', this) === false){
5638 var ce = this.el.select('.navbar-collapse',true).first();
5640 if (!ce.hasClass('show')) {
5650 * Expand the navbar pulldown
5652 expand : function ()
5655 var ce = this.el.select('.navbar-collapse',true).first();
5656 if (ce.hasClass('collapsing')) {
5659 ce.dom.style.height = '';
5661 ce.addClass('in'); // old...
5662 ce.removeClass('collapse');
5663 ce.addClass('show');
5664 var h = ce.getHeight();
5666 ce.removeClass('show');
5667 // at this point we should be able to see it..
5668 ce.addClass('collapsing');
5670 ce.setHeight(0); // resize it ...
5671 ce.on('transitionend', function() {
5672 //Roo.log('done transition');
5673 ce.removeClass('collapsing');
5674 ce.addClass('show');
5675 ce.removeClass('collapse');
5677 ce.dom.style.height = '';
5678 }, this, { single: true} );
5680 ce.dom.scrollTop = 0;
5683 * Collapse the navbar pulldown
5685 collapse : function()
5687 var ce = this.el.select('.navbar-collapse',true).first();
5689 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5690 // it's collapsed or collapsing..
5693 ce.removeClass('in'); // old...
5694 ce.setHeight(ce.getHeight());
5695 ce.removeClass('show');
5696 ce.addClass('collapsing');
5698 ce.on('transitionend', function() {
5699 ce.dom.style.height = '';
5700 ce.removeClass('collapsing');
5701 ce.addClass('collapse');
5702 }, this, { single: true} );
5722 * @class Roo.bootstrap.nav.Simplebar
5723 * @extends Roo.bootstrap.nav.Bar
5724 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5725 * Bootstrap Sidebar class
5727 * @cfg {Boolean} inverse is inverted color
5729 * @cfg {String} type (nav | pills | tabs)
5730 * @cfg {Boolean} arrangement stacked | justified
5731 * @cfg {String} align (left | right) alignment
5733 * @cfg {Boolean} main (true|false) main nav bar? default false
5734 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5736 * @cfg {String} tag (header|footer|nav|div) default is nav
5738 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5742 * Create a new Sidebar
5743 * @param {Object} config The config object
5747 Roo.bootstrap.nav.Simplebar = function(config){
5748 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5751 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5767 getAutoCreate : function(){
5771 tag : this.tag || 'div',
5772 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5774 if (['light','white'].indexOf(this.weight) > -1) {
5775 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5777 cfg.cls += ' bg-' + this.weight;
5780 cfg.cls += ' navbar-inverse';
5784 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5786 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5795 cls: 'nav nav-' + this.xtype,
5801 this.type = this.type || 'nav';
5802 if (['tabs','pills'].indexOf(this.type) != -1) {
5803 cfg.cn[0].cls += ' nav-' + this.type
5807 if (this.type!=='nav') {
5808 Roo.log('nav type must be nav/tabs/pills')
5810 cfg.cn[0].cls += ' navbar-nav'
5816 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5817 cfg.cn[0].cls += ' nav-' + this.arrangement;
5821 if (this.align === 'right') {
5822 cfg.cn[0].cls += ' navbar-right';
5847 * navbar-expand-md fixed-top
5851 * @class Roo.bootstrap.nav.Headerbar
5852 * @extends Roo.bootstrap.nav.Simplebar
5853 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5854 * Bootstrap Sidebar class
5856 * @cfg {String} brand what is brand
5857 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5858 * @cfg {String} brand_href href of the brand
5859 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5860 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5861 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5862 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5865 * Create a new Sidebar
5866 * @param {Object} config The config object
5870 Roo.bootstrap.nav.Headerbar = function(config){
5871 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5875 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5882 desktopCenter : false,
5885 getAutoCreate : function(){
5888 tag: this.nav || 'nav',
5889 cls: 'navbar navbar-expand-md',
5895 if (this.desktopCenter) {
5896 cn.push({cls : 'container', cn : []});
5904 cls: 'navbar-toggle navbar-toggler',
5905 'data-toggle': 'collapse',
5910 html: 'Toggle navigation'
5914 cls: 'icon-bar navbar-toggler-icon'
5927 cn.push( Roo.bootstrap.version == 4 ? btn : {
5929 cls: 'navbar-header',
5938 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5942 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5944 if (['light','white'].indexOf(this.weight) > -1) {
5945 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5947 cfg.cls += ' bg-' + this.weight;
5950 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5951 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5953 // tag can override this..
5955 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5958 if (this.brand !== '') {
5959 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5960 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5962 href: this.brand_href ? this.brand_href : '#',
5963 cls: 'navbar-brand',
5971 cfg.cls += ' main-nav';
5979 getHeaderChildContainer : function()
5981 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5982 return this.el.select('.navbar-header',true).first();
5985 return this.getChildContainer();
5988 getChildContainer : function()
5991 return this.el.select('.roo-navbar-collapse',true).first();
5996 initEvents : function()
5998 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
6000 if (this.autohide) {
6005 Roo.get(document).on('scroll',function(e) {
6006 var ns = Roo.get(document).getScroll().top;
6007 var os = prevScroll;
6011 ft.removeClass('slideDown');
6012 ft.addClass('slideUp');
6015 ft.removeClass('slideUp');
6016 ft.addClass('slideDown');
6037 * @class Roo.bootstrap.nav.Sidebar
6038 * @extends Roo.bootstrap.nav.Bar
6039 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6040 * Bootstrap Sidebar class
6043 * Create a new Sidebar
6044 * @param {Object} config The config object
6048 Roo.bootstrap.nav.Sidebar = function(config){
6049 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6052 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6054 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6056 getAutoCreate : function(){
6061 cls: 'sidebar sidebar-nav'
6083 * @class Roo.bootstrap.nav.Group
6084 * @extends Roo.bootstrap.Component
6085 * @children Roo.bootstrap.nav.Item
6086 * Bootstrap NavGroup class
6087 * @cfg {String} align (left|right)
6088 * @cfg {Boolean} inverse
6089 * @cfg {String} type (nav|pills|tab) default nav
6090 * @cfg {String} navId - reference Id for navbar.
6091 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6094 * Create a new nav group
6095 * @param {Object} config The config object
6098 Roo.bootstrap.nav.Group = function(config){
6099 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6102 Roo.bootstrap.nav.Group.register(this);
6106 * Fires when the active item changes
6107 * @param {Roo.bootstrap.nav.Group} this
6108 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6109 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6116 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6128 getAutoCreate : function()
6130 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6136 if (Roo.bootstrap.version == 4) {
6137 if (['tabs','pills'].indexOf(this.type) != -1) {
6138 cfg.cls += ' nav-' + this.type;
6140 // trying to remove so header bar can right align top?
6141 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6142 // do not use on header bar...
6143 cfg.cls += ' navbar-nav';
6148 if (['tabs','pills'].indexOf(this.type) != -1) {
6149 cfg.cls += ' nav-' + this.type
6151 if (this.type !== 'nav') {
6152 Roo.log('nav type must be nav/tabs/pills')
6154 cfg.cls += ' navbar-nav'
6158 if (this.parent() && this.parent().sidebar) {
6161 cls: 'dashboard-menu sidebar-menu'
6167 if (this.form === true) {
6170 cls: 'navbar-form form-inline'
6172 //nav navbar-right ml-md-auto
6173 if (this.align === 'right') {
6174 cfg.cls += ' navbar-right ml-md-auto';
6176 cfg.cls += ' navbar-left';
6180 if (this.align === 'right') {
6181 cfg.cls += ' navbar-right ml-md-auto';
6183 cfg.cls += ' mr-auto';
6187 cfg.cls += ' navbar-inverse';
6195 * sets the active Navigation item
6196 * @param {Roo.bootstrap.nav.Item} the new current navitem
6198 setActiveItem : function(item)
6201 Roo.each(this.navItems, function(v){
6206 v.setActive(false, true);
6213 item.setActive(true, true);
6214 this.fireEvent('changed', this, item, prev);
6219 * gets the active Navigation item
6220 * @return {Roo.bootstrap.nav.Item} the current navitem
6222 getActive : function()
6226 Roo.each(this.navItems, function(v){
6237 indexOfNav : function()
6241 Roo.each(this.navItems, function(v,i){
6252 * adds a Navigation item
6253 * @param {Roo.bootstrap.nav.Item} the navitem to add
6255 addItem : function(cfg)
6257 if (this.form && Roo.bootstrap.version == 4) {
6260 var cn = new Roo.bootstrap.nav.Item(cfg);
6262 cn.parentId = this.id;
6263 cn.onRender(this.el, null);
6267 * register a Navigation item
6268 * @param {Roo.bootstrap.nav.Item} the navitem to add
6270 register : function(item)
6272 this.navItems.push( item);
6273 item.navId = this.navId;
6278 * clear all the Navigation item
6281 clearAll : function()
6284 this.el.dom.innerHTML = '';
6287 getNavItem: function(tabId)
6290 Roo.each(this.navItems, function(e) {
6291 if (e.tabId == tabId) {
6301 setActiveNext : function()
6303 var i = this.indexOfNav(this.getActive());
6304 if (i > this.navItems.length) {
6307 this.setActiveItem(this.navItems[i+1]);
6309 setActivePrev : function()
6311 var i = this.indexOfNav(this.getActive());
6315 this.setActiveItem(this.navItems[i-1]);
6317 clearWasActive : function(except) {
6318 Roo.each(this.navItems, function(e) {
6319 if (e.tabId != except.tabId && e.was_active) {
6320 e.was_active = false;
6327 getWasActive : function ()
6330 Roo.each(this.navItems, function(e) {
6345 Roo.apply(Roo.bootstrap.nav.Group, {
6349 * register a Navigation Group
6350 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6352 register : function(navgrp)
6354 this.groups[navgrp.navId] = navgrp;
6358 * fetch a Navigation Group based on the navigation ID
6359 * @param {string} the navgroup to add
6360 * @returns {Roo.bootstrap.nav.Group} the navgroup
6362 get: function(navId) {
6363 if (typeof(this.groups[navId]) == 'undefined') {
6365 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6367 return this.groups[navId] ;
6375 * @class Roo.bootstrap.nav.Item
6376 * @extends Roo.bootstrap.Component
6377 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6378 * @parent Roo.bootstrap.nav.Group
6380 * Bootstrap Navbar.NavItem class
6382 * @cfg {String} href link to
6383 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6384 * @cfg {Boolean} button_outline show and outlined button
6385 * @cfg {String} html content of button
6386 * @cfg {String} badge text inside badge
6387 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6388 * @cfg {String} glyphicon DEPRICATED - use fa
6389 * @cfg {String} icon DEPRICATED - use fa
6390 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6391 * @cfg {Boolean} active Is item active
6392 * @cfg {Boolean} disabled Is item disabled
6393 * @cfg {String} linkcls Link Class
6394 * @cfg {Boolean} preventDefault (true | false) default false
6395 * @cfg {String} tabId the tab that this item activates.
6396 * @cfg {String} tagtype (a|span) render as a href or span?
6397 * @cfg {Boolean} animateRef (true|false) link to element default false
6398 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6401 * Create a new Navbar Item
6402 * @param {Object} config The config object
6404 Roo.bootstrap.nav.Item = function(config){
6405 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6410 * The raw click event for the entire grid.
6411 * @param {Roo.EventObject} e
6416 * Fires when the active item active state changes
6417 * @param {Roo.bootstrap.nav.Item} this
6418 * @param {boolean} state the new state
6424 * Fires when scroll to element
6425 * @param {Roo.bootstrap.nav.Item} this
6426 * @param {Object} options
6427 * @param {Roo.EventObject} e
6435 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6444 preventDefault : false,
6452 button_outline : false,
6456 getAutoCreate : function(){
6463 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6466 cfg.cls += ' active' ;
6468 if (this.disabled) {
6469 cfg.cls += ' disabled';
6473 if (this.button_weight.length) {
6474 cfg.tag = this.href ? 'a' : 'button';
6475 cfg.html = this.html || '';
6476 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6478 cfg.href = this.href;
6481 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6483 cfg.cls += " nav-html";
6486 // menu .. should add dropdown-menu class - so no need for carat..
6488 if (this.badge !== '') {
6490 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6495 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6499 href : this.href || "#",
6500 html: this.html || '',
6504 if (this.tagtype == 'a') {
6505 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6509 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6510 } else if (this.fa) {
6511 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6512 } else if(this.glyphicon) {
6513 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6515 cfg.cn[0].cls += " nav-html";
6519 cfg.cn[0].html += " <span class='caret'></span>";
6523 if (this.badge !== '') {
6524 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6532 onRender : function(ct, position)
6534 // Roo.log("Call onRender: " + this.xtype);
6535 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6539 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6540 this.navLink = this.el.select('.nav-link',true).first();
6541 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6546 initEvents: function()
6548 if (typeof (this.menu) != 'undefined') {
6549 this.menu.parentType = this.xtype;
6550 this.menu.triggerEl = this.el;
6551 this.menu = this.addxtype(Roo.apply({}, this.menu));
6554 this.el.on('click', this.onClick, this);
6556 //if(this.tagtype == 'span'){
6557 // this.el.select('span',true).on('click', this.onClick, this);
6560 // at this point parent should be available..
6561 this.parent().register(this);
6564 onClick : function(e)
6566 if (e.getTarget('.dropdown-menu-item')) {
6567 // did you click on a menu itemm.... - then don't trigger onclick..
6572 this.preventDefault ||
6573 this.href === false ||
6576 //Roo.log("NavItem - prevent Default?");
6580 if (this.disabled) {
6584 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6585 if (tg && tg.transition) {
6586 Roo.log("waiting for the transitionend");
6592 //Roo.log("fire event clicked");
6593 if(this.fireEvent('click', this, e) === false){
6597 if(this.tagtype == 'span'){
6601 //Roo.log(this.href);
6602 var ael = this.el.select('a',true).first();
6605 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6606 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6607 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6608 return; // ignore... - it's a 'hash' to another page.
6610 Roo.log("NavItem - prevent Default?");
6612 this.scrollToElement(e);
6616 var p = this.parent();
6618 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6619 if (typeof(p.setActiveItem) !== 'undefined') {
6620 p.setActiveItem(this);
6624 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6625 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6626 // remove the collapsed menu expand...
6627 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6631 isActive: function () {
6634 setActive : function(state, fire, is_was_active)
6636 if (this.active && !state && this.navId) {
6637 this.was_active = true;
6638 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6640 nv.clearWasActive(this);
6644 this.active = state;
6647 this.el.removeClass('active');
6648 this.navLink ? this.navLink.removeClass('active') : false;
6649 } else if (!this.el.hasClass('active')) {
6651 this.el.addClass('active');
6652 if (Roo.bootstrap.version == 4 && this.navLink ) {
6653 this.navLink.addClass('active');
6658 this.fireEvent('changed', this, state);
6661 // show a panel if it's registered and related..
6663 if (!this.navId || !this.tabId || !state || is_was_active) {
6667 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6671 var pan = tg.getPanelByName(this.tabId);
6675 // if we can not flip to new panel - go back to old nav highlight..
6676 if (false == tg.showPanel(pan)) {
6677 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6679 var onav = nv.getWasActive();
6681 onav.setActive(true, false, true);
6690 // this should not be here...
6691 setDisabled : function(state)
6693 this.disabled = state;
6695 this.el.removeClass('disabled');
6696 } else if (!this.el.hasClass('disabled')) {
6697 this.el.addClass('disabled');
6703 * Fetch the element to display the tooltip on.
6704 * @return {Roo.Element} defaults to this.el
6706 tooltipEl : function()
6708 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6711 scrollToElement : function(e)
6713 var c = document.body;
6716 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6718 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6719 c = document.documentElement;
6722 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6728 var o = target.calcOffsetsTo(c);
6735 this.fireEvent('scrollto', this, options, e);
6737 Roo.get(c).scrollTo('top', options.value, true);
6742 * Set the HTML (text content) of the item
6743 * @param {string} html content for the nav item
6745 setHtml : function(html)
6748 this.htmlEl.dom.innerHTML = html;
6760 * <span> icon </span>
6761 * <span> text </span>
6762 * <span>badge </span>
6766 * @class Roo.bootstrap.nav.SidebarItem
6767 * @extends Roo.bootstrap.nav.Item
6768 * Bootstrap Navbar.NavSidebarItem class
6770 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6771 * {Boolean} open is the menu open
6772 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6773 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6774 * {String} buttonSize (sm|md|lg)the extra classes for the button
6775 * {Boolean} showArrow show arrow next to the text (default true)
6777 * Create a new Navbar Button
6778 * @param {Object} config The config object
6780 Roo.bootstrap.nav.SidebarItem = function(config){
6781 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6786 * The raw click event for the entire grid.
6787 * @param {Roo.EventObject} e
6792 * Fires when the active item active state changes
6793 * @param {Roo.bootstrap.nav.SidebarItem} this
6794 * @param {boolean} state the new state
6802 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6804 badgeWeight : 'default',
6810 buttonWeight : 'default',
6816 getAutoCreate : function(){
6821 href : this.href || '#',
6827 if(this.buttonView){
6830 href : this.href || '#',
6831 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6844 cfg.cls += ' active';
6847 if (this.disabled) {
6848 cfg.cls += ' disabled';
6851 cfg.cls += ' open x-open';
6854 if (this.glyphicon || this.icon) {
6855 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6856 a.cn.push({ tag : 'i', cls : c }) ;
6859 if(!this.buttonView){
6862 html : this.html || ''
6869 if (this.badge !== '') {
6870 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6876 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6879 a.cls += ' dropdown-toggle treeview' ;
6885 initEvents : function()
6887 if (typeof (this.menu) != 'undefined') {
6888 this.menu.parentType = this.xtype;
6889 this.menu.triggerEl = this.el;
6890 this.menu = this.addxtype(Roo.apply({}, this.menu));
6893 this.el.on('click', this.onClick, this);
6895 if(this.badge !== ''){
6896 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6901 onClick : function(e)
6908 if(this.preventDefault){
6912 this.fireEvent('click', this, e);
6915 disable : function()
6917 this.setDisabled(true);
6922 this.setDisabled(false);
6925 setDisabled : function(state)
6927 if(this.disabled == state){
6931 this.disabled = state;
6934 this.el.addClass('disabled');
6938 this.el.removeClass('disabled');
6943 setActive : function(state)
6945 if(this.active == state){
6949 this.active = state;
6952 this.el.addClass('active');
6956 this.el.removeClass('active');
6961 isActive: function ()
6966 setBadge : function(str)
6972 this.badgeEl.dom.innerHTML = str;
6989 * @class Roo.bootstrap.nav.ProgressBar
6990 * @extends Roo.bootstrap.Component
6991 * @children Roo.bootstrap.nav.ProgressBarItem
6992 * Bootstrap NavProgressBar class
6995 * Create a new nav progress bar - a bar indicating step along a process
6996 * @param {Object} config The config object
6999 Roo.bootstrap.nav.ProgressBar = function(config){
7000 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
7002 this.bullets = this.bullets || [];
7004 // Roo.bootstrap.nav.ProgressBar.register(this);
7008 * Fires when the active item changes
7009 * @param {Roo.bootstrap.nav.ProgressBar} this
7010 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7011 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7018 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7020 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7021 * Bullets for the Nav Progress bar for the toolbar
7026 getAutoCreate : function()
7028 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7032 cls : 'roo-navigation-bar-group',
7036 cls : 'roo-navigation-top-bar'
7040 cls : 'roo-navigation-bullets-bar',
7044 cls : 'roo-navigation-bar'
7051 cls : 'roo-navigation-bottom-bar'
7061 initEvents: function()
7066 onRender : function(ct, position)
7068 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7070 if(this.bullets.length){
7071 Roo.each(this.bullets, function(b){
7080 addItem : function(cfg)
7082 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7084 item.parentId = this.id;
7085 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7088 var top = new Roo.bootstrap.Element({
7090 cls : 'roo-navigation-bar-text'
7093 var bottom = new Roo.bootstrap.Element({
7095 cls : 'roo-navigation-bar-text'
7098 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7099 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7101 var topText = new Roo.bootstrap.Element({
7103 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7106 var bottomText = new Roo.bootstrap.Element({
7108 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7111 topText.onRender(top.el, null);
7112 bottomText.onRender(bottom.el, null);
7115 item.bottomEl = bottom;
7118 this.barItems.push(item);
7123 getActive : function()
7127 Roo.each(this.barItems, function(v){
7129 if (!v.isActive()) {
7141 setActiveItem : function(item)
7145 Roo.each(this.barItems, function(v){
7146 if (v.rid == item.rid) {
7156 item.setActive(true);
7158 this.fireEvent('changed', this, item, prev);
7161 getBarItem: function(rid)
7165 Roo.each(this.barItems, function(e) {
7177 indexOfItem : function(item)
7181 Roo.each(this.barItems, function(v, i){
7183 if (v.rid != item.rid) {
7194 setActiveNext : function()
7196 var i = this.indexOfItem(this.getActive());
7198 if (i > this.barItems.length) {
7202 this.setActiveItem(this.barItems[i+1]);
7205 setActivePrev : function()
7207 var i = this.indexOfItem(this.getActive());
7213 this.setActiveItem(this.barItems[i-1]);
7218 if(!this.barItems.length){
7222 var width = 100 / this.barItems.length;
7224 Roo.each(this.barItems, function(i){
7225 i.el.setStyle('width', width + '%');
7226 i.topEl.el.setStyle('width', width + '%');
7227 i.bottomEl.el.setStyle('width', width + '%');
7241 * @class Roo.bootstrap.nav.ProgressBarItem
7242 * @extends Roo.bootstrap.Component
7243 * Bootstrap NavProgressBarItem class
7244 * @cfg {String} rid the reference id
7245 * @cfg {Boolean} active (true|false) Is item active default false
7246 * @cfg {Boolean} disabled (true|false) Is item active default false
7247 * @cfg {String} html
7248 * @cfg {String} position (top|bottom) text position default bottom
7249 * @cfg {String} icon show icon instead of number
7252 * Create a new NavProgressBarItem
7253 * @param {Object} config The config object
7255 Roo.bootstrap.nav.ProgressBarItem = function(config){
7256 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7261 * The raw click event for the entire grid.
7262 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7263 * @param {Roo.EventObject} e
7270 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7276 position : 'bottom',
7279 getAutoCreate : function()
7281 var iconCls = 'roo-navigation-bar-item-icon';
7283 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7287 cls: 'roo-navigation-bar-item',
7297 cfg.cls += ' active';
7300 cfg.cls += ' disabled';
7306 disable : function()
7308 this.setDisabled(true);
7313 this.setDisabled(false);
7316 initEvents: function()
7318 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7320 this.iconEl.on('click', this.onClick, this);
7323 onClick : function(e)
7331 if(this.fireEvent('click', this, e) === false){
7335 this.parent().setActiveItem(this);
7338 isActive: function ()
7343 setActive : function(state)
7345 if(this.active == state){
7349 this.active = state;
7352 this.el.addClass('active');
7356 this.el.removeClass('active');
7361 setDisabled : function(state)
7363 if(this.disabled == state){
7367 this.disabled = state;
7370 this.el.addClass('disabled');
7374 this.el.removeClass('disabled');
7377 tooltipEl : function()
7379 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7390 Roo.namespace('Roo.bootstrap.breadcrumb');
7394 * @class Roo.bootstrap.breadcrumb.Nav
7395 * @extends Roo.bootstrap.Component
7396 * Bootstrap Breadcrumb Nav Class
7398 * @children Roo.bootstrap.breadcrumb.Item
7401 * Create a new breadcrumb.Nav
7402 * @param {Object} config The config object
7406 Roo.bootstrap.breadcrumb.Nav = function(config){
7407 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7412 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7414 getAutoCreate : function()
7431 initEvents: function()
7433 this.olEl = this.el.select('ol',true).first();
7435 getChildContainer : function()
7451 * @class Roo.bootstrap.breadcrumb.Nav
7452 * @extends Roo.bootstrap.Component
7453 * @children Roo.bootstrap.Component
7454 * @parent Roo.bootstrap.breadcrumb.Nav
7455 * Bootstrap Breadcrumb Nav Class
7458 * @cfg {String} html the content of the link.
7459 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7460 * @cfg {Boolean} active is it active
7464 * Create a new breadcrumb.Nav
7465 * @param {Object} config The config object
7468 Roo.bootstrap.breadcrumb.Item = function(config){
7469 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7474 * The img click event for the img.
7475 * @param {Roo.EventObject} e
7482 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7487 getAutoCreate : function()
7492 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7494 if (this.href !== false) {
7501 cfg.html = this.html;
7507 initEvents: function()
7510 this.el.select('a', true).first().on('click',this.onClick, this)
7514 onClick : function(e)
7517 this.fireEvent('click',this, e);
7530 * @class Roo.bootstrap.Row
7531 * @extends Roo.bootstrap.Component
7532 * @children Roo.bootstrap.Component
7533 * Bootstrap Row class (contains columns...)
7537 * @param {Object} config The config object
7540 Roo.bootstrap.Row = function(config){
7541 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7544 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7546 getAutoCreate : function(){
7565 * @class Roo.bootstrap.Pagination
7566 * @extends Roo.bootstrap.Component
7567 * @children Roo.bootstrap.Pagination
7568 * Bootstrap Pagination class
7570 * @cfg {String} size (xs|sm|md|lg|xl)
7571 * @cfg {Boolean} inverse
7574 * Create a new Pagination
7575 * @param {Object} config The config object
7578 Roo.bootstrap.Pagination = function(config){
7579 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7582 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7588 getAutoCreate : function(){
7594 cfg.cls += ' inverse';
7600 cfg.cls += " " + this.cls;
7618 * @class Roo.bootstrap.PaginationItem
7619 * @extends Roo.bootstrap.Component
7620 * Bootstrap PaginationItem class
7621 * @cfg {String} html text
7622 * @cfg {String} href the link
7623 * @cfg {Boolean} preventDefault (true | false) default true
7624 * @cfg {Boolean} active (true | false) default false
7625 * @cfg {Boolean} disabled default false
7629 * Create a new PaginationItem
7630 * @param {Object} config The config object
7634 Roo.bootstrap.PaginationItem = function(config){
7635 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7640 * The raw click event for the entire grid.
7641 * @param {Roo.EventObject} e
7647 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7651 preventDefault: true,
7656 getAutoCreate : function(){
7662 href : this.href ? this.href : '#',
7663 html : this.html ? this.html : ''
7673 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7677 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7683 initEvents: function() {
7685 this.el.on('click', this.onClick, this);
7688 onClick : function(e)
7690 Roo.log('PaginationItem on click ');
7691 if(this.preventDefault){
7699 this.fireEvent('click', this, e);
7715 * @class Roo.bootstrap.Slider
7716 * @extends Roo.bootstrap.Component
7717 * Bootstrap Slider class
7720 * Create a new Slider
7721 * @param {Object} config The config object
7724 Roo.bootstrap.Slider = function(config){
7725 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7728 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7730 getAutoCreate : function(){
7734 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7738 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7750 * Ext JS Library 1.1.1
7751 * Copyright(c) 2006-2007, Ext JS, LLC.
7753 * Originally Released Under LGPL - original licence link has changed is not relivant.
7756 * <script type="text/javascript">
7759 * @extends Roo.dd.DDProxy
7760 * @class Roo.grid.SplitDragZone
7761 * Support for Column Header resizing
7763 * @param {Object} config
7766 // This is a support class used internally by the Grid components
7767 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7769 this.view = grid.getView();
7770 this.proxy = this.view.resizeProxy;
7771 Roo.grid.SplitDragZone.superclass.constructor.call(
7774 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7776 dragElId : Roo.id(this.proxy.dom),
7781 this.setHandleElId(Roo.id(hd));
7782 if (hd2 !== false) {
7783 this.setOuterHandleElId(Roo.id(hd2));
7786 this.scroll = false;
7788 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7789 fly: Roo.Element.fly,
7791 b4StartDrag : function(x, y){
7792 this.view.headersDisabled = true;
7793 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7794 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7796 this.proxy.setHeight(h);
7798 // for old system colWidth really stored the actual width?
7799 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7800 // which in reality did not work.. - it worked only for fixed sizes
7801 // for resizable we need to use actual sizes.
7802 var w = this.cm.getColumnWidth(this.cellIndex);
7803 if (!this.view.mainWrap) {
7805 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7810 // this was w-this.grid.minColumnWidth;
7811 // doesnt really make sense? - w = thie curren width or the rendered one?
7812 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7813 this.resetConstraints();
7814 this.setXConstraint(minw, 1000);
7815 this.setYConstraint(0, 0);
7816 this.minX = x - minw;
7817 this.maxX = x + 1000;
7819 if (!this.view.mainWrap) { // this is Bootstrap code..
7820 this.getDragEl().style.display='block';
7823 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7827 handleMouseDown : function(e){
7828 ev = Roo.EventObject.setEvent(e);
7829 var t = this.fly(ev.getTarget());
7830 if(t.hasClass("x-grid-split")){
7831 this.cellIndex = this.view.getCellIndex(t.dom);
7833 this.cm = this.grid.colModel;
7834 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7835 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7840 endDrag : function(e){
7841 this.view.headersDisabled = false;
7842 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7843 var diff = endX - this.startPos;
7845 var w = this.cm.getColumnWidth(this.cellIndex);
7846 if (!this.view.mainWrap) {
7849 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7852 autoOffset : function(){
7857 * Ext JS Library 1.1.1
7858 * Copyright(c) 2006-2007, Ext JS, LLC.
7860 * Originally Released Under LGPL - original licence link has changed is not relivant.
7863 * <script type="text/javascript">
7867 * @class Roo.grid.AbstractSelectionModel
7868 * @extends Roo.util.Observable
7870 * Abstract base class for grid SelectionModels. It provides the interface that should be
7871 * implemented by descendant classes. This class should not be directly instantiated.
7874 Roo.grid.AbstractSelectionModel = function(){
7875 this.locked = false;
7876 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7879 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7880 /** @ignore Called by the grid automatically. Do not call directly. */
7881 init : function(grid){
7887 * Locks the selections.
7894 * Unlocks the selections.
7896 unlock : function(){
7897 this.locked = false;
7901 * Returns true if the selections are locked.
7904 isLocked : function(){
7909 * Ext JS Library 1.1.1
7910 * Copyright(c) 2006-2007, Ext JS, LLC.
7912 * Originally Released Under LGPL - original licence link has changed is not relivant.
7915 * <script type="text/javascript">
7918 * @extends Roo.grid.AbstractSelectionModel
7919 * @class Roo.grid.RowSelectionModel
7920 * The default SelectionModel used by {@link Roo.grid.Grid}.
7921 * It supports multiple selections and keyboard selection/navigation.
7923 * @param {Object} config
7925 Roo.grid.RowSelectionModel = function(config){
7926 Roo.apply(this, config);
7927 this.selections = new Roo.util.MixedCollection(false, function(o){
7932 this.lastActive = false;
7936 * @event selectionchange
7937 * Fires when the selection changes
7938 * @param {SelectionModel} this
7940 "selectionchange" : true,
7942 * @event afterselectionchange
7943 * Fires after the selection changes (eg. by key press or clicking)
7944 * @param {SelectionModel} this
7946 "afterselectionchange" : true,
7948 * @event beforerowselect
7949 * Fires when a row is selected being selected, return false to cancel.
7950 * @param {SelectionModel} this
7951 * @param {Number} rowIndex The selected index
7952 * @param {Boolean} keepExisting False if other selections will be cleared
7954 "beforerowselect" : true,
7957 * Fires when a row is selected.
7958 * @param {SelectionModel} this
7959 * @param {Number} rowIndex The selected index
7960 * @param {Roo.data.Record} r The record
7964 * @event rowdeselect
7965 * Fires when a row is deselected.
7966 * @param {SelectionModel} this
7967 * @param {Number} rowIndex The selected index
7969 "rowdeselect" : true
7971 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7972 this.locked = false;
7975 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7977 * @cfg {Boolean} singleSelect
7978 * True to allow selection of only one row at a time (defaults to false)
7980 singleSelect : false,
7983 initEvents : function(){
7985 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7986 this.grid.on("mousedown", this.handleMouseDown, this);
7987 }else{ // allow click to work like normal
7988 this.grid.on("rowclick", this.handleDragableRowClick, this);
7990 // bootstrap does not have a view..
7991 var view = this.grid.view ? this.grid.view : this.grid;
7992 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7995 this.selectPrevious(e.shiftKey);
7996 }else if(this.last !== false && this.lastActive !== false){
7997 var last = this.last;
7998 this.selectRange(this.last, this.lastActive-1);
7999 view.focusRow(this.lastActive);
8004 this.selectFirstRow();
8006 this.fireEvent("afterselectionchange", this);
8008 "down" : function(e){
8010 this.selectNext(e.shiftKey);
8011 }else if(this.last !== false && this.lastActive !== false){
8012 var last = this.last;
8013 this.selectRange(this.last, this.lastActive+1);
8014 view.focusRow(this.lastActive);
8019 this.selectFirstRow();
8021 this.fireEvent("afterselectionchange", this);
8027 view.on("refresh", this.onRefresh, this);
8028 view.on("rowupdated", this.onRowUpdated, this);
8029 view.on("rowremoved", this.onRemove, this);
8033 onRefresh : function(){
8034 var ds = this.grid.ds, i, v = this.grid.view;
8035 var s = this.selections;
8037 if((i = ds.indexOfId(r.id)) != -1){
8039 s.add(ds.getAt(i)); // updating the selection relate data
8047 onRemove : function(v, index, r){
8048 this.selections.remove(r);
8052 onRowUpdated : function(v, index, r){
8053 if(this.isSelected(r)){
8054 v.onRowSelect(index);
8060 * @param {Array} records The records to select
8061 * @param {Boolean} keepExisting (optional) True to keep existing selections
8063 selectRecords : function(records, keepExisting){
8065 this.clearSelections();
8067 var ds = this.grid.ds;
8068 for(var i = 0, len = records.length; i < len; i++){
8069 this.selectRow(ds.indexOf(records[i]), true);
8074 * Gets the number of selected rows.
8077 getCount : function(){
8078 return this.selections.length;
8082 * Selects the first row in the grid.
8084 selectFirstRow : function(){
8089 * Select the last row.
8090 * @param {Boolean} keepExisting (optional) True to keep existing selections
8092 selectLastRow : function(keepExisting){
8093 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8097 * Selects the row immediately following the last selected row.
8098 * @param {Boolean} keepExisting (optional) True to keep existing selections
8100 selectNext : function(keepExisting){
8101 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8102 this.selectRow(this.last+1, keepExisting);
8103 var view = this.grid.view ? this.grid.view : this.grid;
8104 view.focusRow(this.last);
8109 * Selects the row that precedes the last selected row.
8110 * @param {Boolean} keepExisting (optional) True to keep existing selections
8112 selectPrevious : function(keepExisting){
8114 this.selectRow(this.last-1, keepExisting);
8115 var view = this.grid.view ? this.grid.view : this.grid;
8116 view.focusRow(this.last);
8121 * Returns the selected records
8122 * @return {Array} Array of selected records
8124 getSelections : function(){
8125 return [].concat(this.selections.items);
8129 * Returns the first selected record.
8132 getSelected : function(){
8133 return this.selections.itemAt(0);
8138 * Clears all selections.
8140 clearSelections : function(fast){
8145 var ds = this.grid.ds;
8146 var s = this.selections;
8148 this.deselectRow(ds.indexOfId(r.id));
8152 this.selections.clear();
8161 selectAll : function(){
8165 this.selections.clear();
8166 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8167 this.selectRow(i, true);
8172 * Returns True if there is a selection.
8175 hasSelection : function(){
8176 return this.selections.length > 0;
8180 * Returns True if the specified row is selected.
8181 * @param {Number/Record} record The record or index of the record to check
8184 isSelected : function(index){
8185 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8186 return (r && this.selections.key(r.id) ? true : false);
8190 * Returns True if the specified record id is selected.
8191 * @param {String} id The id of record to check
8194 isIdSelected : function(id){
8195 return (this.selections.key(id) ? true : false);
8199 handleMouseDown : function(e, t)
8201 var view = this.grid.view ? this.grid.view : this.grid;
8203 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8206 if(e.shiftKey && this.last !== false){
8207 var last = this.last;
8208 this.selectRange(last, rowIndex, e.ctrlKey);
8209 this.last = last; // reset the last
8210 view.focusRow(rowIndex);
8212 var isSelected = this.isSelected(rowIndex);
8213 if(e.button !== 0 && isSelected){
8214 view.focusRow(rowIndex);
8215 }else if(e.ctrlKey && isSelected){
8216 this.deselectRow(rowIndex);
8217 }else if(!isSelected){
8218 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8219 view.focusRow(rowIndex);
8222 this.fireEvent("afterselectionchange", this);
8225 handleDragableRowClick : function(grid, rowIndex, e)
8227 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8228 this.selectRow(rowIndex, false);
8229 var view = this.grid.view ? this.grid.view : this.grid;
8230 view.focusRow(rowIndex);
8231 this.fireEvent("afterselectionchange", this);
8236 * Selects multiple rows.
8237 * @param {Array} rows Array of the indexes of the row to select
8238 * @param {Boolean} keepExisting (optional) True to keep existing selections
8240 selectRows : function(rows, keepExisting){
8242 this.clearSelections();
8244 for(var i = 0, len = rows.length; i < len; i++){
8245 this.selectRow(rows[i], true);
8250 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8251 * @param {Number} startRow The index of the first row in the range
8252 * @param {Number} endRow The index of the last row in the range
8253 * @param {Boolean} keepExisting (optional) True to retain existing selections
8255 selectRange : function(startRow, endRow, keepExisting){
8260 this.clearSelections();
8262 if(startRow <= endRow){
8263 for(var i = startRow; i <= endRow; i++){
8264 this.selectRow(i, true);
8267 for(var i = startRow; i >= endRow; i--){
8268 this.selectRow(i, true);
8274 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8275 * @param {Number} startRow The index of the first row in the range
8276 * @param {Number} endRow The index of the last row in the range
8278 deselectRange : function(startRow, endRow, preventViewNotify){
8282 for(var i = startRow; i <= endRow; i++){
8283 this.deselectRow(i, preventViewNotify);
8289 * @param {Number} row The index of the row to select
8290 * @param {Boolean} keepExisting (optional) True to keep existing selections
8292 selectRow : function(index, keepExisting, preventViewNotify){
8293 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8296 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8297 if(!keepExisting || this.singleSelect){
8298 this.clearSelections();
8300 var r = this.grid.ds.getAt(index);
8301 this.selections.add(r);
8302 this.last = this.lastActive = index;
8303 if(!preventViewNotify){
8304 var view = this.grid.view ? this.grid.view : this.grid;
8305 view.onRowSelect(index);
8307 this.fireEvent("rowselect", this, index, r);
8308 this.fireEvent("selectionchange", this);
8314 * @param {Number} row The index of the row to deselect
8316 deselectRow : function(index, preventViewNotify){
8320 if(this.last == index){
8323 if(this.lastActive == index){
8324 this.lastActive = false;
8326 var r = this.grid.ds.getAt(index);
8327 this.selections.remove(r);
8328 if(!preventViewNotify){
8329 var view = this.grid.view ? this.grid.view : this.grid;
8330 view.onRowDeselect(index);
8332 this.fireEvent("rowdeselect", this, index);
8333 this.fireEvent("selectionchange", this);
8337 restoreLast : function(){
8339 this.last = this._last;
8344 acceptsNav : function(row, col, cm){
8345 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8349 onEditorKey : function(field, e){
8350 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8355 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8357 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8359 }else if(k == e.ENTER && !e.ctrlKey){
8363 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8365 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8367 }else if(k == e.ESC){
8371 g.startEditing(newCell[0], newCell[1]);
8376 * Ext JS Library 1.1.1
8377 * Copyright(c) 2006-2007, Ext JS, LLC.
8379 * Originally Released Under LGPL - original licence link has changed is not relivant.
8382 * <script type="text/javascript">
8387 * @class Roo.grid.ColumnModel
8388 * @extends Roo.util.Observable
8389 * This is the default implementation of a ColumnModel used by the Grid. It defines
8390 * the columns in the grid.
8393 var colModel = new Roo.grid.ColumnModel([
8394 {header: "Ticker", width: 60, sortable: true, locked: true},
8395 {header: "Company Name", width: 150, sortable: true},
8396 {header: "Market Cap.", width: 100, sortable: true},
8397 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8398 {header: "Employees", width: 100, sortable: true, resizable: false}
8403 * The config options listed for this class are options which may appear in each
8404 * individual column definition.
8405 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8407 * @param {Object} config An Array of column config objects. See this class's
8408 * config objects for details.
8410 Roo.grid.ColumnModel = function(config){
8412 * The config passed into the constructor
8414 this.config = []; //config;
8417 // if no id, create one
8418 // if the column does not have a dataIndex mapping,
8419 // map it to the order it is in the config
8420 for(var i = 0, len = config.length; i < len; i++){
8421 this.addColumn(config[i]);
8426 * The width of columns which have no width specified (defaults to 100)
8429 this.defaultWidth = 100;
8432 * Default sortable of columns which have no sortable specified (defaults to false)
8435 this.defaultSortable = false;
8439 * @event widthchange
8440 * Fires when the width of a column changes.
8441 * @param {ColumnModel} this
8442 * @param {Number} columnIndex The column index
8443 * @param {Number} newWidth The new width
8445 "widthchange": true,
8447 * @event headerchange
8448 * Fires when the text of a header changes.
8449 * @param {ColumnModel} this
8450 * @param {Number} columnIndex The column index
8451 * @param {Number} newText The new header text
8453 "headerchange": true,
8455 * @event hiddenchange
8456 * Fires when a column is hidden or "unhidden".
8457 * @param {ColumnModel} this
8458 * @param {Number} columnIndex The column index
8459 * @param {Boolean} hidden true if hidden, false otherwise
8461 "hiddenchange": true,
8463 * @event columnmoved
8464 * Fires when a column is moved.
8465 * @param {ColumnModel} this
8466 * @param {Number} oldIndex
8467 * @param {Number} newIndex
8469 "columnmoved" : true,
8471 * @event columlockchange
8472 * Fires when a column's locked state is changed
8473 * @param {ColumnModel} this
8474 * @param {Number} colIndex
8475 * @param {Boolean} locked true if locked
8477 "columnlockchange" : true
8479 Roo.grid.ColumnModel.superclass.constructor.call(this);
8481 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8483 * @cfg {String} header [required] The header text to display in the Grid view.
8486 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8489 * @cfg {String} smHeader Header at Bootsrap Small width
8492 * @cfg {String} mdHeader Header at Bootsrap Medium width
8495 * @cfg {String} lgHeader Header at Bootsrap Large width
8498 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8501 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8502 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8503 * specified, the column's index is used as an index into the Record's data Array.
8506 * @cfg {Number} width The initial width in pixels of the column. Using this
8507 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8510 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8511 * Defaults to the value of the {@link #defaultSortable} property.
8512 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8515 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8518 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8521 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8524 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8527 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8528 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8529 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8530 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8533 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8536 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8539 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8542 * @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)
8545 * @cfg {String} tooltip mouse over tooltip text
8548 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8551 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8554 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8557 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8560 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8563 * Returns the id of the column at the specified index.
8564 * @param {Number} index The column index
8565 * @return {String} the id
8567 getColumnId : function(index){
8568 return this.config[index].id;
8572 * Returns the column for a specified id.
8573 * @param {String} id The column id
8574 * @return {Object} the column
8576 getColumnById : function(id){
8577 return this.lookup[id];
8582 * Returns the column Object for a specified dataIndex.
8583 * @param {String} dataIndex The column dataIndex
8584 * @return {Object|Boolean} the column or false if not found
8586 getColumnByDataIndex: function(dataIndex){
8587 var index = this.findColumnIndex(dataIndex);
8588 return index > -1 ? this.config[index] : false;
8592 * Returns the index for a specified column id.
8593 * @param {String} id The column id
8594 * @return {Number} the index, or -1 if not found
8596 getIndexById : function(id){
8597 for(var i = 0, len = this.config.length; i < len; i++){
8598 if(this.config[i].id == id){
8606 * Returns the index for a specified column dataIndex.
8607 * @param {String} dataIndex The column dataIndex
8608 * @return {Number} the index, or -1 if not found
8611 findColumnIndex : function(dataIndex){
8612 for(var i = 0, len = this.config.length; i < len; i++){
8613 if(this.config[i].dataIndex == dataIndex){
8621 moveColumn : function(oldIndex, newIndex){
8622 var c = this.config[oldIndex];
8623 this.config.splice(oldIndex, 1);
8624 this.config.splice(newIndex, 0, c);
8625 this.dataMap = null;
8626 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8629 isLocked : function(colIndex){
8630 return this.config[colIndex].locked === true;
8633 setLocked : function(colIndex, value, suppressEvent){
8634 if(this.isLocked(colIndex) == value){
8637 this.config[colIndex].locked = value;
8639 this.fireEvent("columnlockchange", this, colIndex, value);
8643 getTotalLockedWidth : function(){
8645 for(var i = 0; i < this.config.length; i++){
8646 if(this.isLocked(i) && !this.isHidden(i)){
8647 this.totalWidth += this.getColumnWidth(i);
8653 getLockedCount : function(){
8654 for(var i = 0, len = this.config.length; i < len; i++){
8655 if(!this.isLocked(i)){
8660 return this.config.length;
8664 * Returns the number of columns.
8667 getColumnCount : function(visibleOnly){
8668 if(visibleOnly === true){
8670 for(var i = 0, len = this.config.length; i < len; i++){
8671 if(!this.isHidden(i)){
8677 return this.config.length;
8681 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8682 * @param {Function} fn
8683 * @param {Object} scope (optional)
8684 * @return {Array} result
8686 getColumnsBy : function(fn, scope){
8688 for(var i = 0, len = this.config.length; i < len; i++){
8689 var c = this.config[i];
8690 if(fn.call(scope||this, c, i) === true){
8698 * Returns true if the specified column is sortable.
8699 * @param {Number} col The column index
8702 isSortable : function(col){
8703 if(typeof this.config[col].sortable == "undefined"){
8704 return this.defaultSortable;
8706 return this.config[col].sortable;
8710 * Returns the rendering (formatting) function defined for the column.
8711 * @param {Number} col The column index.
8712 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8714 getRenderer : function(col){
8715 if(!this.config[col].renderer){
8716 return Roo.grid.ColumnModel.defaultRenderer;
8718 return this.config[col].renderer;
8722 * Sets the rendering (formatting) function for a column.
8723 * @param {Number} col The column index
8724 * @param {Function} fn The function to use to process the cell's raw data
8725 * to return HTML markup for the grid view. The render function is called with
8726 * the following parameters:<ul>
8727 * <li>Data value.</li>
8728 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8729 * <li>css A CSS style string to apply to the table cell.</li>
8730 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8731 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8732 * <li>Row index</li>
8733 * <li>Column index</li>
8734 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8736 setRenderer : function(col, fn){
8737 this.config[col].renderer = fn;
8741 * Returns the width for the specified column.
8742 * @param {Number} col The column index
8743 * @param (optional) {String} gridSize bootstrap width size.
8746 getColumnWidth : function(col, gridSize)
8748 var cfg = this.config[col];
8750 if (typeof(gridSize) == 'undefined') {
8751 return cfg.width * 1 || this.defaultWidth;
8753 if (gridSize === false) { // if we set it..
8754 return cfg.width || false;
8756 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8758 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8759 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8762 return cfg[ sizes[i] ];
8769 * Sets the width for a column.
8770 * @param {Number} col The column index
8771 * @param {Number} width The new width
8773 setColumnWidth : function(col, width, suppressEvent){
8774 this.config[col].width = width;
8775 this.totalWidth = null;
8777 this.fireEvent("widthchange", this, col, width);
8782 * Returns the total width of all columns.
8783 * @param {Boolean} includeHidden True to include hidden column widths
8786 getTotalWidth : function(includeHidden){
8787 if(!this.totalWidth){
8788 this.totalWidth = 0;
8789 for(var i = 0, len = this.config.length; i < len; i++){
8790 if(includeHidden || !this.isHidden(i)){
8791 this.totalWidth += this.getColumnWidth(i);
8795 return this.totalWidth;
8799 * Returns the header for the specified column.
8800 * @param {Number} col The column index
8803 getColumnHeader : function(col){
8804 return this.config[col].header;
8808 * Sets the header for a column.
8809 * @param {Number} col The column index
8810 * @param {String} header The new header
8812 setColumnHeader : function(col, header){
8813 this.config[col].header = header;
8814 this.fireEvent("headerchange", this, col, header);
8818 * Returns the tooltip for the specified column.
8819 * @param {Number} col The column index
8822 getColumnTooltip : function(col){
8823 return this.config[col].tooltip;
8826 * Sets the tooltip for a column.
8827 * @param {Number} col The column index
8828 * @param {String} tooltip The new tooltip
8830 setColumnTooltip : function(col, tooltip){
8831 this.config[col].tooltip = tooltip;
8835 * Returns the dataIndex for the specified column.
8836 * @param {Number} col The column index
8839 getDataIndex : function(col){
8840 return this.config[col].dataIndex;
8844 * Sets the dataIndex for a column.
8845 * @param {Number} col The column index
8846 * @param {Number} dataIndex The new dataIndex
8848 setDataIndex : function(col, dataIndex){
8849 this.config[col].dataIndex = dataIndex;
8855 * Returns true if the cell is editable.
8856 * @param {Number} colIndex The column index
8857 * @param {Number} rowIndex The row index - this is nto actually used..?
8860 isCellEditable : function(colIndex, rowIndex){
8861 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8865 * Returns the editor defined for the cell/column.
8866 * return false or null to disable editing.
8867 * @param {Number} colIndex The column index
8868 * @param {Number} rowIndex The row index
8871 getCellEditor : function(colIndex, rowIndex){
8872 return this.config[colIndex].editor;
8876 * Sets if a column is editable.
8877 * @param {Number} col The column index
8878 * @param {Boolean} editable True if the column is editable
8880 setEditable : function(col, editable){
8881 this.config[col].editable = editable;
8886 * Returns true if the column is hidden.
8887 * @param {Number} colIndex The column index
8890 isHidden : function(colIndex){
8891 return this.config[colIndex].hidden;
8896 * Returns true if the column width cannot be changed
8898 isFixed : function(colIndex){
8899 return this.config[colIndex].fixed;
8903 * Returns true if the column can be resized
8906 isResizable : function(colIndex){
8907 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8910 * Sets if a column is hidden.
8911 * @param {Number} colIndex The column index
8912 * @param {Boolean} hidden True if the column is hidden
8914 setHidden : function(colIndex, hidden){
8915 this.config[colIndex].hidden = hidden;
8916 this.totalWidth = null;
8917 this.fireEvent("hiddenchange", this, colIndex, hidden);
8921 * Sets the editor for a column.
8922 * @param {Number} col The column index
8923 * @param {Object} editor The editor object
8925 setEditor : function(col, editor){
8926 this.config[col].editor = editor;
8929 * Add a column (experimental...) - defaults to adding to the end..
8930 * @param {Object} config
8932 addColumn : function(c)
8935 var i = this.config.length;
8938 if(typeof c.dataIndex == "undefined"){
8941 if(typeof c.renderer == "string"){
8942 c.renderer = Roo.util.Format[c.renderer];
8944 if(typeof c.id == "undefined"){
8947 if(c.editor && c.editor.xtype){
8948 c.editor = Roo.factory(c.editor, Roo.grid);
8950 if(c.editor && c.editor.isFormField){
8951 c.editor = new Roo.grid.GridEditor(c.editor);
8953 this.lookup[c.id] = c;
8958 Roo.grid.ColumnModel.defaultRenderer = function(value)
8960 if(typeof value == "object") {
8963 if(typeof value == "string" && value.length < 1){
8967 return String.format("{0}", value);
8970 // Alias for backwards compatibility
8971 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8974 * Ext JS Library 1.1.1
8975 * Copyright(c) 2006-2007, Ext JS, LLC.
8977 * Originally Released Under LGPL - original licence link has changed is not relivant.
8980 * <script type="text/javascript">
8984 * @class Roo.LoadMask
8985 * A simple utility class for generically masking elements while loading data. If the element being masked has
8986 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8987 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8988 * element's UpdateManager load indicator and will be destroyed after the initial load.
8990 * Create a new LoadMask
8991 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8992 * @param {Object} config The config object
8994 Roo.LoadMask = function(el, config){
8995 this.el = Roo.get(el);
8996 Roo.apply(this, config);
8998 this.store.on('beforeload', this.onBeforeLoad, this);
8999 this.store.on('load', this.onLoad, this);
9000 this.store.on('loadexception', this.onLoadException, this);
9001 this.removeMask = false;
9003 var um = this.el.getUpdateManager();
9004 um.showLoadIndicator = false; // disable the default indicator
9005 um.on('beforeupdate', this.onBeforeLoad, this);
9006 um.on('update', this.onLoad, this);
9007 um.on('failure', this.onLoad, this);
9008 this.removeMask = true;
9012 Roo.LoadMask.prototype = {
9014 * @cfg {Boolean} removeMask
9015 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9016 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9021 * The text to display in a centered loading message box (defaults to 'Loading...')
9025 * @cfg {String} msgCls
9026 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9028 msgCls : 'x-mask-loading',
9031 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9037 * Disables the mask to prevent it from being displayed
9039 disable : function(){
9040 this.disabled = true;
9044 * Enables the mask so that it can be displayed
9046 enable : function(){
9047 this.disabled = false;
9050 onLoadException : function()
9054 if (typeof(arguments[3]) != 'undefined') {
9055 Roo.MessageBox.alert("Error loading",arguments[3]);
9059 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9060 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9067 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9072 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9076 onBeforeLoad : function(){
9078 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9083 destroy : function(){
9085 this.store.un('beforeload', this.onBeforeLoad, this);
9086 this.store.un('load', this.onLoad, this);
9087 this.store.un('loadexception', this.onLoadException, this);
9089 var um = this.el.getUpdateManager();
9090 um.un('beforeupdate', this.onBeforeLoad, this);
9091 um.un('update', this.onLoad, this);
9092 um.un('failure', this.onLoad, this);
9096 * @class Roo.bootstrap.Table
9098 * @extends Roo.bootstrap.Component
9099 * @children Roo.bootstrap.TableBody
9100 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9101 * Similar to Roo.grid.Grid
9103 var table = Roo.factory({
9105 xns : Roo.bootstrap,
9106 autoSizeColumns: true,
9113 sortInfo : { direction : 'ASC', field: 'name' },
9115 xtype : 'HttpProxy',
9118 url : 'https://example.com/some.data.url.json'
9121 xtype : 'JsonReader',
9123 fields : [ 'id', 'name', whatever' ],
9130 xtype : 'ColumnModel',
9134 dataIndex : 'is_in_group',
9137 renderer : function(v, x , r) {
9139 return String.format("{0}", v)
9145 xtype : 'RowSelectionModel',
9146 xns : Roo.bootstrap.Table
9147 // you can add listeners to catch selection change here....
9153 grid.render(Roo.get("some-div"));
9156 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9161 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9162 * @cfg {Roo.data.Store} store The data store to use
9163 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9165 * @cfg {String} cls table class
9168 * @cfg {string} empty_results Text to display for no results
9169 * @cfg {boolean} striped Should the rows be alternative striped
9170 * @cfg {boolean} bordered Add borders to the table
9171 * @cfg {boolean} hover Add hover highlighting
9172 * @cfg {boolean} condensed Format condensed
9173 * @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,
9174 * also adds table-responsive (see bootstrap docs for details)
9175 * @cfg {Boolean} loadMask (true|false) default false
9176 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9177 * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9178 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9179 * @cfg {Boolean} rowSelection (true|false) default false
9180 * @cfg {Boolean} cellSelection (true|false) default false
9181 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9182 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9183 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9184 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9185 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9186 * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9189 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9192 * Create a new Table
9193 * @param {Object} config The config object
9196 Roo.bootstrap.Table = function(config)
9198 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9201 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9202 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9203 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9204 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9206 this.view = this; // compat with grid.
9208 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9210 this.sm.grid = this;
9211 this.selModel = Roo.factory(this.sm, Roo.grid);
9212 this.sm = this.selModel;
9213 this.sm.xmodule = this.xmodule || false;
9216 if (this.cm && typeof(this.cm.config) == 'undefined') {
9217 this.colModel = new Roo.grid.ColumnModel(this.cm);
9218 this.cm = this.colModel;
9219 this.cm.xmodule = this.xmodule || false;
9222 this.store= Roo.factory(this.store, Roo.data);
9223 this.ds = this.store;
9224 this.ds.xmodule = this.xmodule || false;
9227 if (this.footer && this.store) {
9228 this.footer.dataSource = this.ds;
9229 this.footer = Roo.factory(this.footer);
9236 * Fires when a cell is clicked
9237 * @param {Roo.bootstrap.Table} this
9238 * @param {Roo.Element} el
9239 * @param {Number} rowIndex
9240 * @param {Number} columnIndex
9241 * @param {Roo.EventObject} e
9245 * @event celldblclick
9246 * Fires when a cell is double clicked
9247 * @param {Roo.bootstrap.Table} this
9248 * @param {Roo.Element} el
9249 * @param {Number} rowIndex
9250 * @param {Number} columnIndex
9251 * @param {Roo.EventObject} e
9253 "celldblclick" : true,
9256 * Fires when a row is clicked
9257 * @param {Roo.bootstrap.Table} this
9258 * @param {Roo.Element} el
9259 * @param {Number} rowIndex
9260 * @param {Roo.EventObject} e
9264 * @event rowdblclick
9265 * Fires when a row is double clicked
9266 * @param {Roo.bootstrap.Table} this
9267 * @param {Roo.Element} el
9268 * @param {Number} rowIndex
9269 * @param {Roo.EventObject} e
9271 "rowdblclick" : true,
9274 * Fires when a mouseover occur
9275 * @param {Roo.bootstrap.Table} this
9276 * @param {Roo.Element} el
9277 * @param {Number} rowIndex
9278 * @param {Number} columnIndex
9279 * @param {Roo.EventObject} e
9284 * Fires when a mouseout occur
9285 * @param {Roo.bootstrap.Table} this
9286 * @param {Roo.Element} el
9287 * @param {Number} rowIndex
9288 * @param {Number} columnIndex
9289 * @param {Roo.EventObject} e
9294 * Fires when a row is rendered, so you can change add a style to it.
9295 * @param {Roo.bootstrap.Table} this
9296 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9300 * @event rowsrendered
9301 * Fires when all the rows have been rendered
9302 * @param {Roo.bootstrap.Table} this
9304 'rowsrendered' : true,
9306 * @event contextmenu
9307 * The raw contextmenu event for the entire grid.
9308 * @param {Roo.EventObject} e
9310 "contextmenu" : true,
9312 * @event rowcontextmenu
9313 * Fires when a row is right clicked
9314 * @param {Roo.bootstrap.Table} this
9315 * @param {Number} rowIndex
9316 * @param {Roo.EventObject} e
9318 "rowcontextmenu" : true,
9320 * @event cellcontextmenu
9321 * Fires when a cell is right clicked
9322 * @param {Roo.bootstrap.Table} this
9323 * @param {Number} rowIndex
9324 * @param {Number} cellIndex
9325 * @param {Roo.EventObject} e
9327 "cellcontextmenu" : true,
9329 * @event headercontextmenu
9330 * Fires when a header is right clicked
9331 * @param {Roo.bootstrap.Table} this
9332 * @param {Number} columnIndex
9333 * @param {Roo.EventObject} e
9335 "headercontextmenu" : true,
9338 * The raw mousedown event for the entire grid.
9339 * @param {Roo.EventObject} e
9346 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9364 enableColumnResize: true,
9365 disableAutoSize: false,
9367 rowSelection : false,
9368 cellSelection : false,
9371 minColumnWidth : 50,
9373 // Roo.Element - the tbody
9374 bodyEl: false, // <tbody> Roo.Element - thead element
9375 headEl: false, // <thead> Roo.Element - thead element
9376 resizeProxy : false, // proxy element for dragging?
9380 container: false, // used by gridpanel...
9386 auto_hide_footer : false,
9388 view: false, // actually points to this..
9390 getAutoCreate : function()
9392 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9399 // this get's auto added by panel.Grid
9400 if (this.scrollBody) {
9401 cfg.cls += ' table-body-fixed';
9404 cfg.cls += ' table-striped';
9408 cfg.cls += ' table-hover';
9410 if (this.bordered) {
9411 cfg.cls += ' table-bordered';
9413 if (this.condensed) {
9414 cfg.cls += ' table-condensed';
9417 if (this.responsive) {
9418 cfg.cls += ' table-responsive';
9422 cfg.cls+= ' ' +this.cls;
9428 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9431 if(this.store || this.cm){
9432 if(this.headerShow){
9433 cfg.cn.push(this.renderHeader());
9436 cfg.cn.push(this.renderBody());
9438 if(this.footerShow || this.footerRow){
9439 cfg.cn.push(this.renderFooter());
9442 // where does this come from?
9443 //cfg.cls+= ' TableGrid';
9446 return { cn : [ cfg ] };
9449 initEvents : function()
9451 if(!this.store || !this.cm){
9454 if (this.selModel) {
9455 this.selModel.initEvents();
9459 //Roo.log('initEvents with ds!!!!');
9461 this.bodyEl = this.el.select('tbody', true).first();
9462 this.headEl = this.el.select('thead', true).first();
9463 this.mainFoot = this.el.select('tfoot', true).first();
9468 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9469 e.on('click', this.sort, this);
9473 // why is this done????? = it breaks dialogs??
9474 //this.parent().el.setStyle('position', 'relative');
9478 this.footer.parentId = this.id;
9479 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9482 this.el.select('tfoot tr td').first().addClass('hide');
9487 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9490 this.store.on('load', this.onLoad, this);
9491 this.store.on('beforeload', this.onBeforeLoad, this);
9492 this.store.on('update', this.onUpdate, this);
9493 this.store.on('add', this.onAdd, this);
9494 this.store.on("clear", this.clear, this);
9496 this.el.on("contextmenu", this.onContextMenu, this);
9499 this.cm.on("headerchange", this.onHeaderChange, this);
9500 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9502 //?? does bodyEl get replaced on render?
9503 this.bodyEl.on("click", this.onClick, this);
9504 this.bodyEl.on("dblclick", this.onDblClick, this);
9505 this.bodyEl.on('scroll', this.onBodyScroll, this);
9507 // guessing mainbody will work - this relays usually caught by selmodel at present.
9508 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9511 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9514 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9515 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9520 // Compatibility with grid - we implement all the view features at present.
9521 getView : function()
9526 initCSS : function()
9528 if(this.disableAutoSize) {
9532 var cm = this.cm, styles = [];
9533 this.CSS.removeStyleSheet(this.id + '-cssrules');
9534 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9535 // we can honour xs/sm/md/xl as widths...
9536 // we first have to decide what widht we are currently at...
9537 var sz = Roo.getGridSize();
9541 var cols = []; // visable cols.
9543 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9544 var w = cm.getColumnWidth(i, false);
9546 cols.push( { rel : false, abs : 0 });
9550 cols.push( { rel : false, abs : w });
9552 last = i; // not really..
9555 var w = cm.getColumnWidth(i, sz);
9560 cols.push( { rel : w, abs : false });
9563 var avail = this.bodyEl.dom.clientWidth - total_abs;
9565 var unitWidth = Math.floor(avail / total);
9566 var rem = avail - (unitWidth * total);
9568 var hidden, width, pos = 0 , splithide , left;
9569 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9571 hidden = 'display:none;';
9573 width = 'width:0px;';
9575 if(!cm.isHidden(i)){
9579 // we can honour xs/sm/md/xl ?
9580 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9582 hidden = 'display:none;';
9584 // width should return a small number...
9586 w+=rem; // add the remaining with..
9589 left = "left:" + (pos -4) + "px;";
9590 width = "width:" + w+ "px;";
9593 if (this.responsive) {
9596 hidden = cm.isHidden(i) ? 'display:none;' : '';
9597 splithide = 'display: none;';
9600 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9603 splithide = 'display:none;';
9606 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9607 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9608 // this is the popover version..
9609 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9614 //Roo.log(styles.join(''));
9615 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9621 onContextMenu : function(e, t)
9623 this.processEvent("contextmenu", e);
9626 processEvent : function(name, e)
9628 if (name != 'touchstart' ) {
9629 this.fireEvent(name, e);
9632 var t = e.getTarget();
9634 var cell = Roo.get(t);
9640 if(cell.findParent('tfoot', false, true)){
9644 if(cell.findParent('thead', false, true)){
9646 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9647 cell = Roo.get(t).findParent('th', false, true);
9649 Roo.log("failed to find th in thead?");
9650 Roo.log(e.getTarget());
9655 var cellIndex = cell.dom.cellIndex;
9657 var ename = name == 'touchstart' ? 'click' : name;
9658 this.fireEvent("header" + ename, this, cellIndex, e);
9663 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9664 cell = Roo.get(t).findParent('td', false, true);
9666 Roo.log("failed to find th in tbody?");
9667 Roo.log(e.getTarget());
9672 var row = cell.findParent('tr', false, true);
9673 var cellIndex = cell.dom.cellIndex;
9674 var rowIndex = row.dom.rowIndex - 1;
9678 this.fireEvent("row" + name, this, rowIndex, e);
9682 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9688 onMouseover : function(e, el)
9690 var cell = Roo.get(el);
9696 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9697 cell = cell.findParent('td', false, true);
9700 var row = cell.findParent('tr', false, true);
9701 var cellIndex = cell.dom.cellIndex;
9702 var rowIndex = row.dom.rowIndex - 1; // start from 0
9704 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9708 onMouseout : function(e, el)
9710 var cell = Roo.get(el);
9716 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9717 cell = cell.findParent('td', false, true);
9720 var row = cell.findParent('tr', false, true);
9721 var cellIndex = cell.dom.cellIndex;
9722 var rowIndex = row.dom.rowIndex - 1; // start from 0
9724 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9728 onClick : function(e, el)
9730 var cell = Roo.get(el);
9732 if(!cell || (!this.cellSelection && !this.rowSelection)){
9736 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9737 cell = cell.findParent('td', false, true);
9740 if(!cell || typeof(cell) == 'undefined'){
9744 var row = cell.findParent('tr', false, true);
9746 if(!row || typeof(row) == 'undefined'){
9750 var cellIndex = cell.dom.cellIndex;
9751 var rowIndex = this.getRowIndex(row);
9753 // why??? - should these not be based on SelectionModel?
9754 //if(this.cellSelection){
9755 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9758 //if(this.rowSelection){
9759 this.fireEvent('rowclick', this, row, rowIndex, e);
9764 onDblClick : function(e,el)
9766 var cell = Roo.get(el);
9768 if(!cell || (!this.cellSelection && !this.rowSelection)){
9772 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9773 cell = cell.findParent('td', false, true);
9776 if(!cell || typeof(cell) == 'undefined'){
9780 var row = cell.findParent('tr', false, true);
9782 if(!row || typeof(row) == 'undefined'){
9786 var cellIndex = cell.dom.cellIndex;
9787 var rowIndex = this.getRowIndex(row);
9789 if(this.cellSelection){
9790 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9793 if(this.rowSelection){
9794 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9797 findRowIndex : function(el)
9799 var cell = Roo.get(el);
9803 var row = cell.findParent('tr', false, true);
9805 if(!row || typeof(row) == 'undefined'){
9808 return this.getRowIndex(row);
9810 sort : function(e,el)
9812 var col = Roo.get(el);
9814 if(!col.hasClass('sortable')){
9818 var sort = col.attr('sort');
9821 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9825 this.store.sortInfo = {field : sort, direction : dir};
9828 Roo.log("calling footer first");
9829 this.footer.onClick('first');
9832 this.store.load({ params : { start : 0 } });
9836 renderHeader : function()
9844 this.totalWidth = 0;
9846 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9848 var config = cm.config[i];
9852 cls : 'x-hcol-' + i,
9855 html: cm.getColumnHeader(i)
9858 var tooltip = cm.getColumnTooltip(i);
9860 c.tooltip = tooltip;
9866 if(typeof(config.sortable) != 'undefined' && config.sortable){
9867 c.cls += ' sortable';
9868 c.html = '<i class="fa"></i>' + c.html;
9871 // could use BS4 hidden-..-down
9873 if(typeof(config.lgHeader) != 'undefined'){
9874 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9877 if(typeof(config.mdHeader) != 'undefined'){
9878 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9881 if(typeof(config.smHeader) != 'undefined'){
9882 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9885 if(typeof(config.xsHeader) != 'undefined'){
9886 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9893 if(typeof(config.tooltip) != 'undefined'){
9894 c.tooltip = config.tooltip;
9897 if(typeof(config.colspan) != 'undefined'){
9898 c.colspan = config.colspan;
9901 // hidden is handled by CSS now
9903 if(typeof(config.dataIndex) != 'undefined'){
9904 c.sort = config.dataIndex;
9909 if(typeof(config.align) != 'undefined' && config.align.length){
9910 c.style += ' text-align:' + config.align + ';';
9913 /* width is done in CSS
9914 *if(typeof(config.width) != 'undefined'){
9915 c.style += ' width:' + config.width + 'px;';
9916 this.totalWidth += config.width;
9918 this.totalWidth += 100; // assume minimum of 100 per column?
9922 if(typeof(config.cls) != 'undefined'){
9923 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9925 // this is the bit that doesnt reall work at all...
9927 if (this.responsive) {
9930 ['xs','sm','md','lg'].map(function(size){
9932 if(typeof(config[size]) == 'undefined'){
9936 if (!config[size]) { // 0 = hidden
9937 // BS 4 '0' is treated as hide that column and below.
9938 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9942 c.cls += ' col-' + size + '-' + config[size] + (
9943 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9951 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9962 renderBody : function()
9972 colspan : this.cm.getColumnCount()
9982 renderFooter : function()
9992 colspan : this.cm.getColumnCount()
10002 onLoad : function()
10004 // Roo.log('ds onload');
10009 var ds = this.store;
10011 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10012 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10013 if (_this.store.sortInfo) {
10015 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10016 e.select('i', true).addClass(['fa-arrow-up']);
10019 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10020 e.select('i', true).addClass(['fa-arrow-down']);
10025 var tbody = this.bodyEl;
10027 if(ds.getCount() > 0){
10028 ds.data.each(function(d,rowIndex){
10029 var row = this.renderRow(cm, ds, rowIndex);
10031 tbody.createChild(row);
10035 if(row.cellObjects.length){
10036 Roo.each(row.cellObjects, function(r){
10037 _this.renderCellObject(r);
10042 } else if (this.empty_results.length) {
10043 this.el.mask(this.empty_results, 'no-spinner');
10046 var tfoot = this.el.select('tfoot', true).first();
10048 if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10050 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10052 var total = this.ds.getTotalCount();
10054 if(this.footer.pageSize < total){
10055 this.mainFoot.show();
10059 if(!this.footerShow && this.footerRow) {
10066 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10067 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10070 cls : ' x-fcol-' + i,
10078 tfoot.dom.innerHTML = '';
10080 tfoot.createChild(tr);
10083 Roo.each(this.el.select('tbody td', true).elements, function(e){
10084 e.on('mouseover', _this.onMouseover, _this);
10087 Roo.each(this.el.select('tbody td', true).elements, function(e){
10088 e.on('mouseout', _this.onMouseout, _this);
10090 this.fireEvent('rowsrendered', this);
10094 this.initCSS(); /// resize cols
10100 onUpdate : function(ds,record)
10102 this.refreshRow(record);
10106 onRemove : function(ds, record, index, isUpdate){
10107 if(isUpdate !== true){
10108 this.fireEvent("beforerowremoved", this, index, record);
10110 var bt = this.bodyEl.dom;
10112 var rows = this.el.select('tbody > tr', true).elements;
10114 if(typeof(rows[index]) != 'undefined'){
10115 bt.removeChild(rows[index].dom);
10118 // if(bt.rows[index]){
10119 // bt.removeChild(bt.rows[index]);
10122 if(isUpdate !== true){
10123 //this.stripeRows(index);
10124 //this.syncRowHeights(index, index);
10126 this.fireEvent("rowremoved", this, index, record);
10130 onAdd : function(ds, records, rowIndex)
10132 //Roo.log('on Add called');
10133 // - note this does not handle multiple adding very well..
10134 var bt = this.bodyEl.dom;
10135 for (var i =0 ; i < records.length;i++) {
10136 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10137 //Roo.log(records[i]);
10138 //Roo.log(this.store.getAt(rowIndex+i));
10139 this.insertRow(this.store, rowIndex + i, false);
10146 refreshRow : function(record){
10147 var ds = this.store, index;
10148 if(typeof record == 'number'){
10150 record = ds.getAt(index);
10152 index = ds.indexOf(record);
10154 return; // should not happen - but seems to
10157 this.insertRow(ds, index, true);
10159 this.onRemove(ds, record, index+1, true);
10161 //this.syncRowHeights(index, index);
10163 this.fireEvent("rowupdated", this, index, record);
10165 // private - called by RowSelection
10166 onRowSelect : function(rowIndex){
10167 var row = this.getRowDom(rowIndex);
10168 row.addClass(['bg-info','info']);
10170 // private - called by RowSelection
10171 onRowDeselect : function(rowIndex)
10173 if (rowIndex < 0) {
10176 var row = this.getRowDom(rowIndex);
10177 row.removeClass(['bg-info','info']);
10180 * Focuses the specified row.
10181 * @param {Number} row The row index
10183 focusRow : function(row)
10185 //Roo.log('GridView.focusRow');
10186 var x = this.bodyEl.dom.scrollLeft;
10187 this.focusCell(row, 0, false);
10188 this.bodyEl.dom.scrollLeft = x;
10192 * Focuses the specified cell.
10193 * @param {Number} row The row index
10194 * @param {Number} col The column index
10195 * @param {Boolean} hscroll false to disable horizontal scrolling
10197 focusCell : function(row, col, hscroll)
10199 //Roo.log('GridView.focusCell');
10200 var el = this.ensureVisible(row, col, hscroll);
10201 // not sure what focusEL achives = it's a <a> pos relative
10202 //this.focusEl.alignTo(el, "tl-tl");
10204 // this.focusEl.focus();
10206 // this.focusEl.focus.defer(1, this.focusEl);
10211 * Scrolls the specified cell into view
10212 * @param {Number} row The row index
10213 * @param {Number} col The column index
10214 * @param {Boolean} hscroll false to disable horizontal scrolling
10216 ensureVisible : function(row, col, hscroll)
10218 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10219 //return null; //disable for testing.
10220 if(typeof row != "number"){
10221 row = row.rowIndex;
10223 if(row < 0 && row >= this.ds.getCount()){
10226 col = (col !== undefined ? col : 0);
10228 while(cm.isHidden(col)){
10232 var el = this.getCellDom(row, col);
10236 var c = this.bodyEl.dom;
10238 var ctop = parseInt(el.offsetTop, 10);
10239 var cleft = parseInt(el.offsetLeft, 10);
10240 var cbot = ctop + el.offsetHeight;
10241 var cright = cleft + el.offsetWidth;
10243 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10244 var ch = 0; //?? header is not withing the area?
10245 var stop = parseInt(c.scrollTop, 10);
10246 var sleft = parseInt(c.scrollLeft, 10);
10247 var sbot = stop + ch;
10248 var sright = sleft + c.clientWidth;
10250 Roo.log('GridView.ensureVisible:' +
10252 ' c.clientHeight:' + c.clientHeight +
10253 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10261 c.scrollTop = ctop;
10262 //Roo.log("set scrolltop to ctop DISABLE?");
10263 }else if(cbot > sbot){
10264 //Roo.log("set scrolltop to cbot-ch");
10265 c.scrollTop = cbot-ch;
10268 if(hscroll !== false){
10270 c.scrollLeft = cleft;
10271 }else if(cright > sright){
10272 c.scrollLeft = cright-c.clientWidth;
10280 insertRow : function(dm, rowIndex, isUpdate){
10283 this.fireEvent("beforerowsinserted", this, rowIndex);
10285 //var s = this.getScrollState();
10286 var row = this.renderRow(this.cm, this.store, rowIndex);
10287 // insert before rowIndex..
10288 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10292 if(row.cellObjects.length){
10293 Roo.each(row.cellObjects, function(r){
10294 _this.renderCellObject(r);
10299 this.fireEvent("rowsinserted", this, rowIndex);
10300 //this.syncRowHeights(firstRow, lastRow);
10301 //this.stripeRows(firstRow);
10308 getRowDom : function(rowIndex)
10310 var rows = this.el.select('tbody > tr', true).elements;
10312 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10315 getCellDom : function(rowIndex, colIndex)
10317 var row = this.getRowDom(rowIndex);
10318 if (row === false) {
10321 var cols = row.select('td', true).elements;
10322 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10326 // returns the object tree for a tr..
10329 renderRow : function(cm, ds, rowIndex)
10331 var d = ds.getAt(rowIndex);
10335 cls : 'x-row-' + rowIndex,
10339 var cellObjects = [];
10341 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10342 var config = cm.config[i];
10344 var renderer = cm.getRenderer(i);
10348 if(typeof(renderer) !== 'undefined'){
10349 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10351 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10352 // and are rendered into the cells after the row is rendered - using the id for the element.
10354 if(typeof(value) === 'object'){
10364 rowIndex : rowIndex,
10369 this.fireEvent('rowclass', this, rowcfg);
10373 // this might end up displaying HTML?
10374 // this is too messy... - better to only do it on columsn you know are going to be too long
10375 //tooltip : (typeof(value) === 'object') ? '' : value,
10376 cls : rowcfg.rowClass + ' x-col-' + i,
10378 html: (typeof(value) === 'object') ? '' : value
10385 if(typeof(config.colspan) != 'undefined'){
10386 td.colspan = config.colspan;
10391 if(typeof(config.align) != 'undefined' && config.align.length){
10392 td.style += ' text-align:' + config.align + ';';
10394 if(typeof(config.valign) != 'undefined' && config.valign.length){
10395 td.style += ' vertical-align:' + config.valign + ';';
10398 if(typeof(config.width) != 'undefined'){
10399 td.style += ' width:' + config.width + 'px;';
10403 if(typeof(config.cursor) != 'undefined'){
10404 td.style += ' cursor:' + config.cursor + ';';
10407 if(typeof(config.cls) != 'undefined'){
10408 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10410 if (this.responsive) {
10411 ['xs','sm','md','lg'].map(function(size){
10413 if(typeof(config[size]) == 'undefined'){
10419 if (!config[size]) { // 0 = hidden
10420 // BS 4 '0' is treated as hide that column and below.
10421 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10425 td.cls += ' col-' + size + '-' + config[size] + (
10426 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10436 row.cellObjects = cellObjects;
10444 onBeforeLoad : function()
10446 this.el.unmask(); // if needed.
10453 this.el.select('tbody', true).first().dom.innerHTML = '';
10456 * Show or hide a row.
10457 * @param {Number} rowIndex to show or hide
10458 * @param {Boolean} state hide
10460 setRowVisibility : function(rowIndex, state)
10462 var bt = this.bodyEl.dom;
10464 var rows = this.el.select('tbody > tr', true).elements;
10466 if(typeof(rows[rowIndex]) == 'undefined'){
10469 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10474 getSelectionModel : function(){
10475 if(!this.selModel){
10476 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10478 return this.selModel;
10481 * Render the Roo.bootstrap object from renderder
10483 renderCellObject : function(r)
10487 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10489 var t = r.cfg.render(r.container);
10492 Roo.each(r.cfg.cn, function(c){
10494 container: t.getChildContainer(),
10497 _this.renderCellObject(child);
10502 * get the Row Index from a dom element.
10503 * @param {Roo.Element} row The row to look for
10504 * @returns {Number} the row
10506 getRowIndex : function(row)
10510 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10521 * get the header TH element for columnIndex
10522 * @param {Number} columnIndex
10523 * @returns {Roo.Element}
10525 getHeaderIndex: function(colIndex)
10527 var cols = this.headEl.select('th', true).elements;
10528 return cols[colIndex];
10531 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10532 * @param {domElement} cell to look for
10533 * @returns {Number} the column
10535 getCellIndex : function(cell)
10537 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10539 return parseInt(id[1], 10);
10544 * Returns the grid's underlying element = used by panel.Grid
10545 * @return {Element} The element
10547 getGridEl : function(){
10551 * Forces a resize - used by panel.Grid
10552 * @return {Element} The element
10554 autoSize : function()
10556 if(this.disableAutoSize) {
10559 //var ctr = Roo.get(this.container.dom.parentElement);
10560 var ctr = Roo.get(this.el.dom);
10562 var thd = this.getGridEl().select('thead',true).first();
10563 var tbd = this.getGridEl().select('tbody', true).first();
10564 var tfd = this.getGridEl().select('tfoot', true).first();
10566 var cw = ctr.getWidth();
10567 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10571 tbd.setWidth(ctr.getWidth());
10572 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10573 // this needs fixing for various usage - currently only hydra job advers I think..
10575 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10577 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10580 cw = Math.max(cw, this.totalWidth);
10581 this.getGridEl().select('tbody tr',true).setWidth(cw);
10584 // resize 'expandable coloumn?
10586 return; // we doe not have a view in this design..
10589 onBodyScroll: function()
10591 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10593 this.headEl.setStyle({
10594 'position' : 'relative',
10595 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10601 var scrollHeight = this.bodyEl.dom.scrollHeight;
10603 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10605 var height = this.bodyEl.getHeight();
10607 if(scrollHeight - height == scrollTop) {
10609 var total = this.ds.getTotalCount();
10611 if(this.footer.cursor + this.footer.pageSize < total){
10613 this.footer.ds.load({
10615 start : this.footer.cursor + this.footer.pageSize,
10616 limit : this.footer.pageSize
10625 onColumnSplitterMoved : function(i, diff)
10627 this.userResized = true;
10629 var cm = this.colModel;
10631 var w = this.getHeaderIndex(i).getWidth() + diff;
10634 cm.setColumnWidth(i, w, true);
10636 //var cid = cm.getColumnId(i); << not used in this version?
10637 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10639 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10640 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10641 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10643 //this.updateSplitters();
10644 //this.layout(); << ??
10645 this.fireEvent("columnresize", i, w);
10647 onHeaderChange : function()
10649 var header = this.renderHeader();
10650 var table = this.el.select('table', true).first();
10652 this.headEl.remove();
10653 this.headEl = table.createChild(header, this.bodyEl, false);
10655 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10656 e.on('click', this.sort, this);
10659 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10660 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10665 onHiddenChange : function(colModel, colIndex, hidden)
10668 this.cm.setHidden()
10669 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10670 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10672 this.CSS.updateRule(thSelector, "display", "");
10673 this.CSS.updateRule(tdSelector, "display", "");
10676 this.CSS.updateRule(thSelector, "display", "none");
10677 this.CSS.updateRule(tdSelector, "display", "none");
10680 // onload calls initCSS()
10681 this.onHeaderChange();
10685 setColumnWidth: function(col_index, width)
10687 // width = "md-2 xs-2..."
10688 if(!this.colModel.config[col_index]) {
10692 var w = width.split(" ");
10694 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10696 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10699 for(var j = 0; j < w.length; j++) {
10705 var size_cls = w[j].split("-");
10707 if(!Number.isInteger(size_cls[1] * 1)) {
10711 if(!this.colModel.config[col_index][size_cls[0]]) {
10715 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10719 h_row[0].classList.replace(
10720 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10721 "col-"+size_cls[0]+"-"+size_cls[1]
10724 for(var i = 0; i < rows.length; i++) {
10726 var size_cls = w[j].split("-");
10728 if(!Number.isInteger(size_cls[1] * 1)) {
10732 if(!this.colModel.config[col_index][size_cls[0]]) {
10736 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10740 rows[i].classList.replace(
10741 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10742 "col-"+size_cls[0]+"-"+size_cls[1]
10746 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10751 // currently only used to find the split on drag..
10752 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10757 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10758 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10767 * @class Roo.bootstrap.TableCell
10768 * @extends Roo.bootstrap.Component
10769 * @children Roo.bootstrap.Component
10770 * @parent Roo.bootstrap.TableRow
10771 * Bootstrap TableCell class
10773 * @cfg {String} html cell contain text
10774 * @cfg {String} cls cell class
10775 * @cfg {String} tag cell tag (td|th) default td
10776 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10777 * @cfg {String} align Aligns the content in a cell
10778 * @cfg {String} axis Categorizes cells
10779 * @cfg {String} bgcolor Specifies the background color of a cell
10780 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10781 * @cfg {Number} colspan Specifies the number of columns a cell should span
10782 * @cfg {String} headers Specifies one or more header cells a cell is related to
10783 * @cfg {Number} height Sets the height of a cell
10784 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10785 * @cfg {Number} rowspan Sets the number of rows a cell should span
10786 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10787 * @cfg {String} valign Vertical aligns the content in a cell
10788 * @cfg {Number} width Specifies the width of a cell
10791 * Create a new TableCell
10792 * @param {Object} config The config object
10795 Roo.bootstrap.TableCell = function(config){
10796 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10799 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10819 getAutoCreate : function(){
10820 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10827 cfg.tag = this.tag;
10840 cfg.align=this.align
10845 if (this.bgcolor) {
10846 cfg.bgcolor=this.bgcolor
10848 if (this.charoff) {
10849 cfg.charoff=this.charoff
10851 if (this.colspan) {
10852 cfg.colspan=this.colspan
10854 if (this.headers) {
10855 cfg.headers=this.headers
10858 cfg.height=this.height
10861 cfg.nowrap=this.nowrap
10863 if (this.rowspan) {
10864 cfg.rowspan=this.rowspan
10867 cfg.scope=this.scope
10870 cfg.valign=this.valign
10873 cfg.width=this.width
10892 * @class Roo.bootstrap.TableRow
10893 * @extends Roo.bootstrap.Component
10894 * @children Roo.bootstrap.TableCell
10895 * @parent Roo.bootstrap.TableBody
10896 * Bootstrap TableRow class
10897 * @cfg {String} cls row class
10898 * @cfg {String} align Aligns the content in a table row
10899 * @cfg {String} bgcolor Specifies a background color for a table row
10900 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10901 * @cfg {String} valign Vertical aligns the content in a table row
10904 * Create a new TableRow
10905 * @param {Object} config The config object
10908 Roo.bootstrap.TableRow = function(config){
10909 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10912 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10920 getAutoCreate : function(){
10921 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10928 cfg.cls = this.cls;
10931 cfg.align = this.align;
10934 cfg.bgcolor = this.bgcolor;
10937 cfg.charoff = this.charoff;
10940 cfg.valign = this.valign;
10958 * @class Roo.bootstrap.TableBody
10959 * @extends Roo.bootstrap.Component
10960 * @children Roo.bootstrap.TableRow
10961 * @parent Roo.bootstrap.Table
10962 * Bootstrap TableBody class
10963 * @cfg {String} cls element class
10964 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10965 * @cfg {String} align Aligns the content inside the element
10966 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10967 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10970 * Create a new TableBody
10971 * @param {Object} config The config object
10974 Roo.bootstrap.TableBody = function(config){
10975 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10978 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10986 getAutoCreate : function(){
10987 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10997 cfg.tag = this.tag;
11001 cfg.align = this.align;
11004 cfg.charoff = this.charoff;
11007 cfg.valign = this.valign;
11014 // initEvents : function()
11017 // if(!this.store){
11021 // this.store = Roo.factory(this.store, Roo.data);
11022 // this.store.on('load', this.onLoad, this);
11024 // this.store.load();
11028 // onLoad: function ()
11030 // this.fireEvent('load', this);
11040 * Ext JS Library 1.1.1
11041 * Copyright(c) 2006-2007, Ext JS, LLC.
11043 * Originally Released Under LGPL - original licence link has changed is not relivant.
11046 * <script type="text/javascript">
11049 // as we use this in bootstrap.
11050 Roo.namespace('Roo.form');
11052 * @class Roo.form.Action
11053 * Internal Class used to handle form actions
11055 * @param {Roo.form.BasicForm} el The form element or its id
11056 * @param {Object} config Configuration options
11061 // define the action interface
11062 Roo.form.Action = function(form, options){
11064 this.options = options || {};
11067 * Client Validation Failed
11070 Roo.form.Action.CLIENT_INVALID = 'client';
11072 * Server Validation Failed
11075 Roo.form.Action.SERVER_INVALID = 'server';
11077 * Connect to Server Failed
11080 Roo.form.Action.CONNECT_FAILURE = 'connect';
11082 * Reading Data from Server Failed
11085 Roo.form.Action.LOAD_FAILURE = 'load';
11087 Roo.form.Action.prototype = {
11089 failureType : undefined,
11090 response : undefined,
11091 result : undefined,
11093 // interface method
11094 run : function(options){
11098 // interface method
11099 success : function(response){
11103 // interface method
11104 handleResponse : function(response){
11108 // default connection failure
11109 failure : function(response){
11111 this.response = response;
11112 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11113 this.form.afterAction(this, false);
11116 processResponse : function(response){
11117 this.response = response;
11118 if(!response.responseText){
11121 this.result = this.handleResponse(response);
11122 return this.result;
11125 // utility functions used internally
11126 getUrl : function(appendParams){
11127 var url = this.options.url || this.form.url || this.form.el.dom.action;
11129 var p = this.getParams();
11131 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11137 getMethod : function(){
11138 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11141 getParams : function(){
11142 var bp = this.form.baseParams;
11143 var p = this.options.params;
11145 if(typeof p == "object"){
11146 p = Roo.urlEncode(Roo.applyIf(p, bp));
11147 }else if(typeof p == 'string' && bp){
11148 p += '&' + Roo.urlEncode(bp);
11151 p = Roo.urlEncode(bp);
11156 createCallback : function(){
11158 success: this.success,
11159 failure: this.failure,
11161 timeout: (this.form.timeout*1000),
11162 upload: this.form.fileUpload ? this.success : undefined
11167 Roo.form.Action.Submit = function(form, options){
11168 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11171 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11174 haveProgress : false,
11175 uploadComplete : false,
11177 // uploadProgress indicator.
11178 uploadProgress : function()
11180 if (!this.form.progressUrl) {
11184 if (!this.haveProgress) {
11185 Roo.MessageBox.progress("Uploading", "Uploading");
11187 if (this.uploadComplete) {
11188 Roo.MessageBox.hide();
11192 this.haveProgress = true;
11194 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11196 var c = new Roo.data.Connection();
11198 url : this.form.progressUrl,
11203 success : function(req){
11204 //console.log(data);
11208 rdata = Roo.decode(req.responseText)
11210 Roo.log("Invalid data from server..");
11214 if (!rdata || !rdata.success) {
11216 Roo.MessageBox.alert(Roo.encode(rdata));
11219 var data = rdata.data;
11221 if (this.uploadComplete) {
11222 Roo.MessageBox.hide();
11227 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11228 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11231 this.uploadProgress.defer(2000,this);
11234 failure: function(data) {
11235 Roo.log('progress url failed ');
11246 // run get Values on the form, so it syncs any secondary forms.
11247 this.form.getValues();
11249 var o = this.options;
11250 var method = this.getMethod();
11251 var isPost = method == 'POST';
11252 if(o.clientValidation === false || this.form.isValid()){
11254 if (this.form.progressUrl) {
11255 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11256 (new Date() * 1) + '' + Math.random());
11261 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11262 form:this.form.el.dom,
11263 url:this.getUrl(!isPost),
11265 params:isPost ? this.getParams() : null,
11266 isUpload: this.form.fileUpload,
11267 formData : this.form.formData
11270 this.uploadProgress();
11272 }else if (o.clientValidation !== false){ // client validation failed
11273 this.failureType = Roo.form.Action.CLIENT_INVALID;
11274 this.form.afterAction(this, false);
11278 success : function(response)
11280 this.uploadComplete= true;
11281 if (this.haveProgress) {
11282 Roo.MessageBox.hide();
11286 var result = this.processResponse(response);
11287 if(result === true || result.success){
11288 this.form.afterAction(this, true);
11292 this.form.markInvalid(result.errors);
11293 this.failureType = Roo.form.Action.SERVER_INVALID;
11295 this.form.afterAction(this, false);
11297 failure : function(response)
11299 this.uploadComplete= true;
11300 if (this.haveProgress) {
11301 Roo.MessageBox.hide();
11304 this.response = response;
11305 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11306 this.form.afterAction(this, false);
11309 handleResponse : function(response){
11310 if(this.form.errorReader){
11311 var rs = this.form.errorReader.read(response);
11314 for(var i = 0, len = rs.records.length; i < len; i++) {
11315 var r = rs.records[i];
11316 errors[i] = r.data;
11319 if(errors.length < 1){
11323 success : rs.success,
11329 var rt = response.responseText;
11330 if (rt.match(/^\<!--\[CDATA\[/)) {
11331 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11332 rt = rt.replace(/\]\]--\>$/,'');
11335 ret = Roo.decode(rt);
11339 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11349 Roo.form.Action.Load = function(form, options){
11350 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11351 this.reader = this.form.reader;
11354 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11359 Roo.Ajax.request(Roo.apply(
11360 this.createCallback(), {
11361 method:this.getMethod(),
11362 url:this.getUrl(false),
11363 params:this.getParams()
11367 success : function(response){
11369 var result = this.processResponse(response);
11370 if(result === true || !result.success || !result.data){
11371 this.failureType = Roo.form.Action.LOAD_FAILURE;
11372 this.form.afterAction(this, false);
11375 this.form.clearInvalid();
11376 this.form.setValues(result.data);
11377 this.form.afterAction(this, true);
11380 handleResponse : function(response){
11381 if(this.form.reader){
11382 var rs = this.form.reader.read(response);
11383 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11385 success : rs.success,
11389 return Roo.decode(response.responseText);
11393 Roo.form.Action.ACTION_TYPES = {
11394 'load' : Roo.form.Action.Load,
11395 'submit' : Roo.form.Action.Submit
11404 * @class Roo.bootstrap.form.Form
11405 * @extends Roo.bootstrap.Component
11406 * @children Roo.bootstrap.Component
11407 * Bootstrap Form class
11408 * @cfg {String} method GET | POST (default POST)
11409 * @cfg {String} labelAlign top | left (default top)
11410 * @cfg {String} align left | right - for navbars
11411 * @cfg {Boolean} loadMask load mask when submit (default true)
11415 * Create a new Form
11416 * @param {Object} config The config object
11420 Roo.bootstrap.form.Form = function(config){
11422 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11424 Roo.bootstrap.form.Form.popover.apply();
11428 * @event clientvalidation
11429 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11430 * @param {Form} this
11431 * @param {Boolean} valid true if the form has passed client-side validation
11433 clientvalidation: true,
11435 * @event beforeaction
11436 * Fires before any action is performed. Return false to cancel the action.
11437 * @param {Form} this
11438 * @param {Action} action The action to be performed
11440 beforeaction: true,
11442 * @event actionfailed
11443 * Fires when an action fails.
11444 * @param {Form} this
11445 * @param {Action} action The action that failed
11447 actionfailed : true,
11449 * @event actioncomplete
11450 * Fires when an action is completed.
11451 * @param {Form} this
11452 * @param {Action} action The action that completed
11454 actioncomplete : true
11458 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11461 * @cfg {String} method
11462 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11466 * @cfg {String} url
11467 * The URL to use for form actions if one isn't supplied in the action options.
11470 * @cfg {Boolean} fileUpload
11471 * Set to true if this form is a file upload.
11475 * @cfg {Object} baseParams
11476 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11480 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11484 * @cfg {Sting} align (left|right) for navbar forms
11489 activeAction : null,
11492 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11493 * element by passing it or its id or mask the form itself by passing in true.
11496 waitMsgTarget : false,
11501 * @cfg {Boolean} errorMask (true|false) default false
11506 * @cfg {Number} maskOffset Default 100
11511 * @cfg {Boolean} maskBody
11515 getAutoCreate : function(){
11519 method : this.method || 'POST',
11520 id : this.id || Roo.id(),
11523 if (this.parent().xtype.match(/^Nav/)) {
11524 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11528 if (this.labelAlign == 'left' ) {
11529 cfg.cls += ' form-horizontal';
11535 initEvents : function()
11537 this.el.on('submit', this.onSubmit, this);
11538 // this was added as random key presses on the form where triggering form submit.
11539 this.el.on('keypress', function(e) {
11540 if (e.getCharCode() != 13) {
11543 // we might need to allow it for textareas.. and some other items.
11544 // check e.getTarget().
11546 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11550 Roo.log("keypress blocked");
11552 e.preventDefault();
11558 onSubmit : function(e){
11563 * Returns true if client-side validation on the form is successful.
11566 isValid : function(){
11567 var items = this.getItems();
11569 var target = false;
11571 items.each(function(f){
11577 Roo.log('invalid field: ' + f.name);
11581 if(!target && f.el.isVisible(true)){
11587 if(this.errorMask && !valid){
11588 Roo.bootstrap.form.Form.popover.mask(this, target);
11595 * Returns true if any fields in this form have changed since their original load.
11598 isDirty : function(){
11600 var items = this.getItems();
11601 items.each(function(f){
11611 * Performs a predefined action (submit or load) or custom actions you define on this form.
11612 * @param {String} actionName The name of the action type
11613 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11614 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11615 * accept other config options):
11617 Property Type Description
11618 ---------------- --------------- ----------------------------------------------------------------------------------
11619 url String The url for the action (defaults to the form's url)
11620 method String The form method to use (defaults to the form's method, or POST if not defined)
11621 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11622 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11623 validate the form on the client (defaults to false)
11625 * @return {BasicForm} this
11627 doAction : function(action, options){
11628 if(typeof action == 'string'){
11629 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11631 if(this.fireEvent('beforeaction', this, action) !== false){
11632 this.beforeAction(action);
11633 action.run.defer(100, action);
11639 beforeAction : function(action){
11640 var o = action.options;
11645 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11647 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11650 // not really supported yet.. ??
11652 //if(this.waitMsgTarget === true){
11653 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11654 //}else if(this.waitMsgTarget){
11655 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11656 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11658 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11664 afterAction : function(action, success){
11665 this.activeAction = null;
11666 var o = action.options;
11671 Roo.get(document.body).unmask();
11677 //if(this.waitMsgTarget === true){
11678 // this.el.unmask();
11679 //}else if(this.waitMsgTarget){
11680 // this.waitMsgTarget.unmask();
11682 // Roo.MessageBox.updateProgress(1);
11683 // Roo.MessageBox.hide();
11690 Roo.callback(o.success, o.scope, [this, action]);
11691 this.fireEvent('actioncomplete', this, action);
11695 // failure condition..
11696 // we have a scenario where updates need confirming.
11697 // eg. if a locking scenario exists..
11698 // we look for { errors : { needs_confirm : true }} in the response.
11700 (typeof(action.result) != 'undefined') &&
11701 (typeof(action.result.errors) != 'undefined') &&
11702 (typeof(action.result.errors.needs_confirm) != 'undefined')
11705 Roo.log("not supported yet");
11708 Roo.MessageBox.confirm(
11709 "Change requires confirmation",
11710 action.result.errorMsg,
11715 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11725 Roo.callback(o.failure, o.scope, [this, action]);
11726 // show an error message if no failed handler is set..
11727 if (!this.hasListener('actionfailed')) {
11728 Roo.log("need to add dialog support");
11730 Roo.MessageBox.alert("Error",
11731 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11732 action.result.errorMsg :
11733 "Saving Failed, please check your entries or try again"
11738 this.fireEvent('actionfailed', this, action);
11743 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11744 * @param {String} id The value to search for
11747 findField : function(id){
11748 var items = this.getItems();
11749 var field = items.get(id);
11751 items.each(function(f){
11752 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11759 return field || null;
11762 * Mark fields in this form invalid in bulk.
11763 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11764 * @return {BasicForm} this
11766 markInvalid : function(errors){
11767 if(errors instanceof Array){
11768 for(var i = 0, len = errors.length; i < len; i++){
11769 var fieldError = errors[i];
11770 var f = this.findField(fieldError.id);
11772 f.markInvalid(fieldError.msg);
11778 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11779 field.markInvalid(errors[id]);
11783 //Roo.each(this.childForms || [], function (f) {
11784 // f.markInvalid(errors);
11791 * Set values for fields in this form in bulk.
11792 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11793 * @return {BasicForm} this
11795 setValues : function(values){
11796 if(values instanceof Array){ // array of objects
11797 for(var i = 0, len = values.length; i < len; i++){
11799 var f = this.findField(v.id);
11801 f.setValue(v.value);
11802 if(this.trackResetOnLoad){
11803 f.originalValue = f.getValue();
11807 }else{ // object hash
11810 if(typeof values[id] != 'function' && (field = this.findField(id))){
11812 if (field.setFromData &&
11813 field.valueField &&
11814 field.displayField &&
11815 // combos' with local stores can
11816 // be queried via setValue()
11817 // to set their value..
11818 (field.store && !field.store.isLocal)
11822 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11823 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11824 field.setFromData(sd);
11826 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11828 field.setFromData(values);
11831 field.setValue(values[id]);
11835 if(this.trackResetOnLoad){
11836 field.originalValue = field.getValue();
11842 //Roo.each(this.childForms || [], function (f) {
11843 // f.setValues(values);
11850 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11851 * they are returned as an array.
11852 * @param {Boolean} asString
11855 getValues : function(asString){
11856 //if (this.childForms) {
11857 // copy values from the child forms
11858 // Roo.each(this.childForms, function (f) {
11859 // this.setValues(f.getValues());
11865 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11866 if(asString === true){
11869 return Roo.urlDecode(fs);
11873 * Returns the fields in this form as an object with key/value pairs.
11874 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11877 getFieldValues : function(with_hidden)
11879 var items = this.getItems();
11881 items.each(function(f){
11883 if (!f.getName()) {
11887 var v = f.getValue();
11889 if (f.inputType =='radio') {
11890 if (typeof(ret[f.getName()]) == 'undefined') {
11891 ret[f.getName()] = ''; // empty..
11894 if (!f.el.dom.checked) {
11898 v = f.el.dom.value;
11902 if(f.xtype == 'MoneyField'){
11903 ret[f.currencyName] = f.getCurrency();
11906 // not sure if this supported any more..
11907 if ((typeof(v) == 'object') && f.getRawValue) {
11908 v = f.getRawValue() ; // dates..
11910 // combo boxes where name != hiddenName...
11911 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11912 ret[f.name] = f.getRawValue();
11914 ret[f.getName()] = v;
11921 * Clears all invalid messages in this form.
11922 * @return {BasicForm} this
11924 clearInvalid : function(){
11925 var items = this.getItems();
11927 items.each(function(f){
11935 * Resets this form.
11936 * @return {BasicForm} this
11938 reset : function(){
11939 var items = this.getItems();
11940 items.each(function(f){
11944 Roo.each(this.childForms || [], function (f) {
11952 getItems : function()
11954 var r=new Roo.util.MixedCollection(false, function(o){
11955 return o.id || (o.id = Roo.id());
11957 var iter = function(el) {
11964 Roo.each(el.items,function(e) {
11973 hideFields : function(items)
11975 Roo.each(items, function(i){
11977 var f = this.findField(i);
11988 showFields : function(items)
11990 Roo.each(items, function(i){
11992 var f = this.findField(i);
12005 Roo.apply(Roo.bootstrap.form.Form, {
12021 intervalID : false,
12027 if(this.isApplied){
12032 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12033 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12034 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12035 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12038 this.maskEl.top.enableDisplayMode("block");
12039 this.maskEl.left.enableDisplayMode("block");
12040 this.maskEl.bottom.enableDisplayMode("block");
12041 this.maskEl.right.enableDisplayMode("block");
12043 this.toolTip = new Roo.bootstrap.Tooltip({
12044 cls : 'roo-form-error-popover',
12046 'left' : ['r-l', [-2,0], 'right'],
12047 'right' : ['l-r', [2,0], 'left'],
12048 'bottom' : ['tl-bl', [0,2], 'top'],
12049 'top' : [ 'bl-tl', [0,-2], 'bottom']
12053 this.toolTip.render(Roo.get(document.body));
12055 this.toolTip.el.enableDisplayMode("block");
12057 Roo.get(document.body).on('click', function(){
12061 Roo.get(document.body).on('touchstart', function(){
12065 this.isApplied = true
12068 mask : function(form, target)
12072 this.target = target;
12074 if(!this.form.errorMask || !target.el){
12078 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12080 Roo.log(scrollable);
12082 var ot = this.target.el.calcOffsetsTo(scrollable);
12084 var scrollTo = ot[1] - this.form.maskOffset;
12086 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12088 scrollable.scrollTo('top', scrollTo);
12090 var box = this.target.el.getBox();
12092 var zIndex = Roo.bootstrap.Modal.zIndex++;
12095 this.maskEl.top.setStyle('position', 'absolute');
12096 this.maskEl.top.setStyle('z-index', zIndex);
12097 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12098 this.maskEl.top.setLeft(0);
12099 this.maskEl.top.setTop(0);
12100 this.maskEl.top.show();
12102 this.maskEl.left.setStyle('position', 'absolute');
12103 this.maskEl.left.setStyle('z-index', zIndex);
12104 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12105 this.maskEl.left.setLeft(0);
12106 this.maskEl.left.setTop(box.y - this.padding);
12107 this.maskEl.left.show();
12109 this.maskEl.bottom.setStyle('position', 'absolute');
12110 this.maskEl.bottom.setStyle('z-index', zIndex);
12111 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12112 this.maskEl.bottom.setLeft(0);
12113 this.maskEl.bottom.setTop(box.bottom + this.padding);
12114 this.maskEl.bottom.show();
12116 this.maskEl.right.setStyle('position', 'absolute');
12117 this.maskEl.right.setStyle('z-index', zIndex);
12118 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12119 this.maskEl.right.setLeft(box.right + this.padding);
12120 this.maskEl.right.setTop(box.y - this.padding);
12121 this.maskEl.right.show();
12123 this.toolTip.bindEl = this.target.el;
12125 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12127 var tip = this.target.blankText;
12129 if(this.target.getValue() !== '' ) {
12131 if (this.target.invalidText.length) {
12132 tip = this.target.invalidText;
12133 } else if (this.target.regexText.length){
12134 tip = this.target.regexText;
12138 this.toolTip.show(tip);
12140 this.intervalID = window.setInterval(function() {
12141 Roo.bootstrap.form.Form.popover.unmask();
12144 window.onwheel = function(){ return false;};
12146 (function(){ this.isMasked = true; }).defer(500, this);
12150 unmask : function()
12152 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12156 this.maskEl.top.setStyle('position', 'absolute');
12157 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12158 this.maskEl.top.hide();
12160 this.maskEl.left.setStyle('position', 'absolute');
12161 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12162 this.maskEl.left.hide();
12164 this.maskEl.bottom.setStyle('position', 'absolute');
12165 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12166 this.maskEl.bottom.hide();
12168 this.maskEl.right.setStyle('position', 'absolute');
12169 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12170 this.maskEl.right.hide();
12172 this.toolTip.hide();
12174 this.toolTip.el.hide();
12176 window.onwheel = function(){ return true;};
12178 if(this.intervalID){
12179 window.clearInterval(this.intervalID);
12180 this.intervalID = false;
12183 this.isMasked = false;
12193 * Ext JS Library 1.1.1
12194 * Copyright(c) 2006-2007, Ext JS, LLC.
12196 * Originally Released Under LGPL - original licence link has changed is not relivant.
12199 * <script type="text/javascript">
12202 * @class Roo.form.VTypes
12203 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12206 Roo.form.VTypes = function(){
12207 // closure these in so they are only created once.
12208 var alpha = /^[a-zA-Z_]+$/;
12209 var alphanum = /^[a-zA-Z0-9_]+$/;
12210 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12211 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12213 // All these messages and functions are configurable
12216 * The function used to validate email addresses
12217 * @param {String} value The email address
12219 email : function(v){
12220 return email.test(v);
12223 * The error text to display when the email validation function returns false
12226 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12228 * The keystroke filter mask to be applied on email input
12231 emailMask : /[a-z0-9_\.\-@]/i,
12234 * The function used to validate URLs
12235 * @param {String} value The URL
12238 return url.test(v);
12241 * The error text to display when the url validation function returns false
12244 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12247 * The function used to validate alpha values
12248 * @param {String} value The value
12250 alpha : function(v){
12251 return alpha.test(v);
12254 * The error text to display when the alpha validation function returns false
12257 alphaText : 'This field should only contain letters and _',
12259 * The keystroke filter mask to be applied on alpha input
12262 alphaMask : /[a-z_]/i,
12265 * The function used to validate alphanumeric values
12266 * @param {String} value The value
12268 alphanum : function(v){
12269 return alphanum.test(v);
12272 * The error text to display when the alphanumeric validation function returns false
12275 alphanumText : 'This field should only contain letters, numbers and _',
12277 * The keystroke filter mask to be applied on alphanumeric input
12280 alphanumMask : /[a-z0-9_]/i
12290 * @class Roo.bootstrap.form.Input
12291 * @extends Roo.bootstrap.Component
12292 * Bootstrap Input class
12293 * @cfg {Boolean} disabled is it disabled
12294 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12295 * @cfg {String} name name of the input
12296 * @cfg {string} fieldLabel - the label associated
12297 * @cfg {string} placeholder - placeholder to put in text.
12298 * @cfg {string} before - input group add on before
12299 * @cfg {string} after - input group add on after
12300 * @cfg {string} size - (lg|sm) or leave empty..
12301 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12302 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12303 * @cfg {Number} md colspan out of 12 for computer-sized screens
12304 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12305 * @cfg {string} value default value of the input
12306 * @cfg {Number} labelWidth set the width of label
12307 * @cfg {Number} labellg set the width of label (1-12)
12308 * @cfg {Number} labelmd set the width of label (1-12)
12309 * @cfg {Number} labelsm set the width of label (1-12)
12310 * @cfg {Number} labelxs set the width of label (1-12)
12311 * @cfg {String} labelAlign (top|left)
12312 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12313 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12314 * @cfg {String} indicatorpos (left|right) default left
12315 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12316 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12317 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12318 * @cfg {Roo.bootstrap.Button} before Button to show before
12319 * @cfg {Roo.bootstrap.Button} afterButton to show before
12320 * @cfg {String} align (left|center|right) Default left
12321 * @cfg {Boolean} forceFeedback (true|false) Default false
12324 * Create a new Input
12325 * @param {Object} config The config object
12328 Roo.bootstrap.form.Input = function(config){
12330 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12335 * Fires when this field receives input focus.
12336 * @param {Roo.form.Field} this
12341 * Fires when this field loses input focus.
12342 * @param {Roo.form.Field} this
12346 * @event specialkey
12347 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12348 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12349 * @param {Roo.form.Field} this
12350 * @param {Roo.EventObject} e The event object
12355 * Fires just before the field blurs if the field value has changed.
12356 * @param {Roo.form.Field} this
12357 * @param {Mixed} newValue The new value
12358 * @param {Mixed} oldValue The original value
12363 * Fires after the field has been marked as invalid.
12364 * @param {Roo.form.Field} this
12365 * @param {String} msg The validation message
12370 * Fires after the field has been validated with no errors.
12371 * @param {Roo.form.Field} this
12376 * Fires after the key up
12377 * @param {Roo.form.Field} this
12378 * @param {Roo.EventObject} e The event Object
12383 * Fires after the user pastes into input
12384 * @param {Roo.form.Field} this
12385 * @param {Roo.EventObject} e The event Object
12391 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12393 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12394 automatic validation (defaults to "keyup").
12396 validationEvent : "keyup",
12398 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12400 validateOnBlur : true,
12402 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12404 validationDelay : 250,
12406 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12408 focusClass : "x-form-focus", // not needed???
12412 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12414 invalidClass : "has-warning",
12417 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12419 validClass : "has-success",
12422 * @cfg {Boolean} hasFeedback (true|false) default true
12424 hasFeedback : true,
12427 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12429 invalidFeedbackClass : "glyphicon-warning-sign",
12432 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12434 validFeedbackClass : "glyphicon-ok",
12437 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12439 selectOnFocus : false,
12442 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12446 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12451 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12453 disableKeyFilter : false,
12456 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12460 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12464 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12466 blankText : "Please complete this mandatory field",
12469 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12473 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12475 maxLength : Number.MAX_VALUE,
12477 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12479 minLengthText : "The minimum length for this field is {0}",
12481 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12483 maxLengthText : "The maximum length for this field is {0}",
12487 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12488 * If available, this function will be called only after the basic validators all return true, and will be passed the
12489 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12493 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12494 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12495 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12499 * @cfg {String} regexText -- Depricated - use Invalid Text
12504 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12510 autocomplete: false,
12514 inputType : 'text',
12517 placeholder: false,
12522 preventMark: false,
12523 isFormField : true,
12526 labelAlign : false,
12529 formatedValue : false,
12530 forceFeedback : false,
12532 indicatorpos : 'left',
12542 parentLabelAlign : function()
12545 while (parent.parent()) {
12546 parent = parent.parent();
12547 if (typeof(parent.labelAlign) !='undefined') {
12548 return parent.labelAlign;
12555 getAutoCreate : function()
12562 if(this.inputType != 'hidden'){
12563 cfg.cls = 'form-group' //input-group
12569 type : this.inputType,
12570 value : this.value,
12571 cls : 'form-control',
12572 placeholder : this.placeholder || '',
12573 autocomplete : this.autocomplete || 'new-password'
12575 if (this.inputType == 'file') {
12576 input.style = 'overflow:hidden'; // why not in CSS?
12579 if(this.capture.length){
12580 input.capture = this.capture;
12583 if(this.accept.length){
12584 input.accept = this.accept + "/*";
12588 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12591 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12592 input.maxLength = this.maxLength;
12595 if (this.disabled) {
12596 input.disabled=true;
12599 if (this.readOnly) {
12600 input.readonly=true;
12604 input.name = this.name;
12608 input.cls += ' input-' + this.size;
12612 ['xs','sm','md','lg'].map(function(size){
12613 if (settings[size]) {
12614 cfg.cls += ' col-' + size + '-' + settings[size];
12618 var inputblock = input;
12622 cls: 'glyphicon form-control-feedback'
12625 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12628 cls : 'has-feedback',
12636 if (this.before || this.after) {
12639 cls : 'input-group',
12643 if (this.before && typeof(this.before) == 'string') {
12645 inputblock.cn.push({
12647 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12651 if (this.before && typeof(this.before) == 'object') {
12652 this.before = Roo.factory(this.before);
12654 inputblock.cn.push({
12656 cls : 'roo-input-before input-group-prepend input-group-' +
12657 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12661 inputblock.cn.push(input);
12663 if (this.after && typeof(this.after) == 'string') {
12664 inputblock.cn.push({
12666 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12670 if (this.after && typeof(this.after) == 'object') {
12671 this.after = Roo.factory(this.after);
12673 inputblock.cn.push({
12675 cls : 'roo-input-after input-group-append input-group-' +
12676 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12680 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12681 inputblock.cls += ' has-feedback';
12682 inputblock.cn.push(feedback);
12688 cfg = this.getAutoCreateLabel( cfg, inputblock );
12693 if (this.parentType === 'Navbar' && this.parent().bar) {
12694 cfg.cls += ' navbar-form';
12697 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12698 // on BS4 we do this only if not form
12699 cfg.cls += ' navbar-form';
12707 * autocreate the label - also used by textara... ?? and others?
12709 getAutoCreateLabel : function( cfg, inputblock )
12711 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12715 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12716 tooltip : 'This field is required'
12718 if (this.allowBlank ) {
12719 indicator.style = this.allowBlank ? ' display:none' : '';
12721 if (align ==='left' && this.fieldLabel.length) {
12723 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12730 cls : 'control-label col-form-label',
12731 html : this.fieldLabel
12742 var labelCfg = cfg.cn[1];
12743 var contentCfg = cfg.cn[2];
12745 if(this.indicatorpos == 'right'){
12750 cls : 'control-label col-form-label',
12754 html : this.fieldLabel
12768 labelCfg = cfg.cn[0];
12769 contentCfg = cfg.cn[1];
12773 if(this.labelWidth > 12){
12774 labelCfg.style = "width: " + this.labelWidth + 'px';
12777 if(this.labelWidth < 13 && this.labelmd == 0){
12778 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12781 if(this.labellg > 0){
12782 labelCfg.cls += ' col-lg-' + this.labellg;
12783 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12786 if(this.labelmd > 0){
12787 labelCfg.cls += ' col-md-' + this.labelmd;
12788 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12791 if(this.labelsm > 0){
12792 labelCfg.cls += ' col-sm-' + this.labelsm;
12793 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12796 if(this.labelxs > 0){
12797 labelCfg.cls += ' col-xs-' + this.labelxs;
12798 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12802 } else if ( this.fieldLabel.length) {
12809 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12810 tooltip : 'This field is required',
12811 style : this.allowBlank ? ' display:none' : ''
12815 //cls : 'input-group-addon',
12816 html : this.fieldLabel
12824 if(this.indicatorpos == 'right'){
12829 //cls : 'input-group-addon',
12830 html : this.fieldLabel
12835 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12836 tooltip : 'This field is required',
12837 style : this.allowBlank ? ' display:none' : ''
12861 * return the real input element.
12863 inputEl: function ()
12865 return this.el.select('input.form-control',true).first();
12868 tooltipEl : function()
12870 return this.inputEl();
12873 indicatorEl : function()
12875 if (Roo.bootstrap.version == 4) {
12876 return false; // not enabled in v4 yet.
12879 var indicator = this.el.select('i.roo-required-indicator',true).first();
12889 setDisabled : function(v)
12891 var i = this.inputEl().dom;
12893 i.removeAttribute('disabled');
12897 i.setAttribute('disabled','true');
12899 initEvents : function()
12902 this.inputEl().on("keydown" , this.fireKey, this);
12903 this.inputEl().on("focus", this.onFocus, this);
12904 this.inputEl().on("blur", this.onBlur, this);
12906 this.inputEl().relayEvent('keyup', this);
12907 this.inputEl().relayEvent('paste', this);
12909 this.indicator = this.indicatorEl();
12911 if(this.indicator){
12912 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12915 // reference to original value for reset
12916 this.originalValue = this.getValue();
12917 //Roo.form.TextField.superclass.initEvents.call(this);
12918 if(this.validationEvent == 'keyup'){
12919 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12920 this.inputEl().on('keyup', this.filterValidation, this);
12922 else if(this.validationEvent !== false){
12923 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12926 if(this.selectOnFocus){
12927 this.on("focus", this.preFocus, this);
12930 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12931 this.inputEl().on("keypress", this.filterKeys, this);
12933 this.inputEl().relayEvent('keypress', this);
12936 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12937 this.el.on("click", this.autoSize, this);
12940 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12941 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12944 if (typeof(this.before) == 'object') {
12945 this.before.render(this.el.select('.roo-input-before',true).first());
12947 if (typeof(this.after) == 'object') {
12948 this.after.render(this.el.select('.roo-input-after',true).first());
12951 this.inputEl().on('change', this.onChange, this);
12954 filterValidation : function(e){
12955 if(!e.isNavKeyPress()){
12956 this.validationTask.delay(this.validationDelay);
12960 * Validates the field value
12961 * @return {Boolean} True if the value is valid, else false
12963 validate : function(){
12964 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12965 if(this.disabled || this.validateValue(this.getRawValue())){
12970 this.markInvalid();
12976 * Validates a value according to the field's validation rules and marks the field as invalid
12977 * if the validation fails
12978 * @param {Mixed} value The value to validate
12979 * @return {Boolean} True if the value is valid, else false
12981 validateValue : function(value)
12983 if(this.getVisibilityEl().hasClass('hidden')){
12987 if(value.length < 1) { // if it's blank
12988 if(this.allowBlank){
12994 if(value.length < this.minLength){
12997 if(value.length > this.maxLength){
13001 var vt = Roo.form.VTypes;
13002 if(!vt[this.vtype](value, this)){
13006 if(typeof this.validator == "function"){
13007 var msg = this.validator(value);
13008 if (typeof(msg) == 'string') {
13009 this.invalidText = msg;
13016 if(this.regex && !this.regex.test(value)){
13024 fireKey : function(e){
13025 //Roo.log('field ' + e.getKey());
13026 if(e.isNavKeyPress()){
13027 this.fireEvent("specialkey", this, e);
13030 focus : function (selectText){
13032 this.inputEl().focus();
13033 if(selectText === true){
13034 this.inputEl().dom.select();
13040 onFocus : function(){
13041 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13042 // this.el.addClass(this.focusClass);
13044 if(!this.hasFocus){
13045 this.hasFocus = true;
13046 this.startValue = this.getValue();
13047 this.fireEvent("focus", this);
13051 beforeBlur : Roo.emptyFn,
13055 onBlur : function(){
13057 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13058 //this.el.removeClass(this.focusClass);
13060 this.hasFocus = false;
13061 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13064 var v = this.getValue();
13065 if(String(v) !== String(this.startValue)){
13066 this.fireEvent('change', this, v, this.startValue);
13068 this.fireEvent("blur", this);
13071 onChange : function(e)
13073 var v = this.getValue();
13074 if(String(v) !== String(this.startValue)){
13075 this.fireEvent('change', this, v, this.startValue);
13081 * Resets the current field value to the originally loaded value and clears any validation messages
13083 reset : function(){
13084 this.setValue(this.originalValue);
13088 * Returns the name of the field
13089 * @return {Mixed} name The name field
13091 getName: function(){
13095 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13096 * @return {Mixed} value The field value
13098 getValue : function(){
13099 var v = this.inputEl().getValue();
13103 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13104 * @return {Mixed} value The field value
13106 getRawValue : function(){
13107 var v = this.inputEl().getValue();
13113 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13114 * @param {Mixed} value The value to set
13116 setRawValue : function(v){
13117 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13120 selectText : function(start, end){
13121 var v = this.getRawValue();
13123 start = start === undefined ? 0 : start;
13124 end = end === undefined ? v.length : end;
13125 var d = this.inputEl().dom;
13126 if(d.setSelectionRange){
13127 d.setSelectionRange(start, end);
13128 }else if(d.createTextRange){
13129 var range = d.createTextRange();
13130 range.moveStart("character", start);
13131 range.moveEnd("character", v.length-end);
13138 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13139 * @param {Mixed} value The value to set
13141 setValue : function(v){
13144 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13150 processValue : function(value){
13151 if(this.stripCharsRe){
13152 var newValue = value.replace(this.stripCharsRe, '');
13153 if(newValue !== value){
13154 this.setRawValue(newValue);
13161 preFocus : function(){
13163 if(this.selectOnFocus){
13164 this.inputEl().dom.select();
13167 filterKeys : function(e){
13168 var k = e.getKey();
13169 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13172 var c = e.getCharCode(), cc = String.fromCharCode(c);
13173 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13176 if(!this.maskRe.test(cc)){
13181 * Clear any invalid styles/messages for this field
13183 clearInvalid : function(){
13185 if(!this.el || this.preventMark){ // not rendered
13190 this.el.removeClass([this.invalidClass, 'is-invalid']);
13192 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13194 var feedback = this.el.select('.form-control-feedback', true).first();
13197 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13202 if(this.indicator){
13203 this.indicator.removeClass('visible');
13204 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13207 this.fireEvent('valid', this);
13211 * Mark this field as valid
13213 markValid : function()
13215 if(!this.el || this.preventMark){ // not rendered...
13219 this.el.removeClass([this.invalidClass, this.validClass]);
13220 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13222 var feedback = this.el.select('.form-control-feedback', true).first();
13225 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13228 if(this.indicator){
13229 this.indicator.removeClass('visible');
13230 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13238 if(this.allowBlank && !this.getRawValue().length){
13241 if (Roo.bootstrap.version == 3) {
13242 this.el.addClass(this.validClass);
13244 this.inputEl().addClass('is-valid');
13247 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13249 var feedback = this.el.select('.form-control-feedback', true).first();
13252 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13253 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13258 this.fireEvent('valid', this);
13262 * Mark this field as invalid
13263 * @param {String} msg The validation message
13265 markInvalid : function(msg)
13267 if(!this.el || this.preventMark){ // not rendered
13271 this.el.removeClass([this.invalidClass, this.validClass]);
13272 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13274 var feedback = this.el.select('.form-control-feedback', true).first();
13277 this.el.select('.form-control-feedback', true).first().removeClass(
13278 [this.invalidFeedbackClass, this.validFeedbackClass]);
13285 if(this.allowBlank && !this.getRawValue().length){
13289 if(this.indicator){
13290 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13291 this.indicator.addClass('visible');
13293 if (Roo.bootstrap.version == 3) {
13294 this.el.addClass(this.invalidClass);
13296 this.inputEl().addClass('is-invalid');
13301 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13303 var feedback = this.el.select('.form-control-feedback', true).first();
13306 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13308 if(this.getValue().length || this.forceFeedback){
13309 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13316 this.fireEvent('invalid', this, msg);
13319 SafariOnKeyDown : function(event)
13321 // this is a workaround for a password hang bug on chrome/ webkit.
13322 if (this.inputEl().dom.type != 'password') {
13326 var isSelectAll = false;
13328 if(this.inputEl().dom.selectionEnd > 0){
13329 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13331 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13332 event.preventDefault();
13337 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13339 event.preventDefault();
13340 // this is very hacky as keydown always get's upper case.
13342 var cc = String.fromCharCode(event.getCharCode());
13343 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13347 adjustWidth : function(tag, w){
13348 tag = tag.toLowerCase();
13349 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13350 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13351 if(tag == 'input'){
13354 if(tag == 'textarea'){
13357 }else if(Roo.isOpera){
13358 if(tag == 'input'){
13361 if(tag == 'textarea'){
13369 setFieldLabel : function(v)
13371 if(!this.rendered){
13375 if(this.indicatorEl()){
13376 var ar = this.el.select('label > span',true);
13378 if (ar.elements.length) {
13379 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13380 this.fieldLabel = v;
13384 var br = this.el.select('label',true);
13386 if(br.elements.length) {
13387 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13388 this.fieldLabel = v;
13392 Roo.log('Cannot Found any of label > span || label in input');
13396 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13397 this.fieldLabel = v;
13412 * @class Roo.bootstrap.form.TextArea
13413 * @extends Roo.bootstrap.form.Input
13414 * Bootstrap TextArea class
13415 * @cfg {Number} cols Specifies the visible width of a text area
13416 * @cfg {Number} rows Specifies the visible number of lines in a text area
13417 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13418 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13419 * @cfg {string} html text
13422 * Create a new TextArea
13423 * @param {Object} config The config object
13426 Roo.bootstrap.form.TextArea = function(config){
13427 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13431 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13441 getAutoCreate : function(){
13443 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13449 if(this.inputType != 'hidden'){
13450 cfg.cls = 'form-group' //input-group
13458 value : this.value || '',
13459 html: this.html || '',
13460 cls : 'form-control',
13461 placeholder : this.placeholder || ''
13465 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13466 input.maxLength = this.maxLength;
13470 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13474 input.cols = this.cols;
13477 if (this.readOnly) {
13478 input.readonly = true;
13482 input.name = this.name;
13486 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13490 ['xs','sm','md','lg'].map(function(size){
13491 if (settings[size]) {
13492 cfg.cls += ' col-' + size + '-' + settings[size];
13496 var inputblock = input;
13498 if(this.hasFeedback && !this.allowBlank){
13502 cls: 'glyphicon form-control-feedback'
13506 cls : 'has-feedback',
13515 if (this.before || this.after) {
13518 cls : 'input-group',
13522 inputblock.cn.push({
13524 cls : 'input-group-addon',
13529 inputblock.cn.push(input);
13531 if(this.hasFeedback && !this.allowBlank){
13532 inputblock.cls += ' has-feedback';
13533 inputblock.cn.push(feedback);
13537 inputblock.cn.push({
13539 cls : 'input-group-addon',
13547 cfg = this.getAutoCreateLabel( cfg, inputblock );
13551 if (this.disabled) {
13552 input.disabled=true;
13559 * return the real textarea element.
13561 inputEl: function ()
13563 return this.el.select('textarea.form-control',true).first();
13567 * Clear any invalid styles/messages for this field
13569 clearInvalid : function()
13572 if(!this.el || this.preventMark){ // not rendered
13576 var label = this.el.select('label', true).first();
13577 //var icon = this.el.select('i.fa-star', true).first();
13579 //if(label && icon){
13582 this.el.removeClass( this.validClass);
13583 this.inputEl().removeClass('is-invalid');
13585 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13587 var feedback = this.el.select('.form-control-feedback', true).first();
13590 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13595 this.fireEvent('valid', this);
13599 * Mark this field as valid
13601 markValid : function()
13603 if(!this.el || this.preventMark){ // not rendered
13607 this.el.removeClass([this.invalidClass, this.validClass]);
13608 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13610 var feedback = this.el.select('.form-control-feedback', true).first();
13613 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13616 if(this.disabled || this.allowBlank){
13620 var label = this.el.select('label', true).first();
13621 var icon = this.el.select('i.fa-star', true).first();
13623 //if(label && icon){
13626 if (Roo.bootstrap.version == 3) {
13627 this.el.addClass(this.validClass);
13629 this.inputEl().addClass('is-valid');
13633 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13635 var feedback = this.el.select('.form-control-feedback', true).first();
13638 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13639 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13644 this.fireEvent('valid', this);
13648 * Mark this field as invalid
13649 * @param {String} msg The validation message
13651 markInvalid : function(msg)
13653 if(!this.el || this.preventMark){ // not rendered
13657 this.el.removeClass([this.invalidClass, this.validClass]);
13658 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13660 var feedback = this.el.select('.form-control-feedback', true).first();
13663 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13670 var label = this.el.select('label', true).first();
13671 //var icon = this.el.select('i.fa-star', true).first();
13673 //if(!this.getValue().length && label && !icon){
13674 /* this.el.createChild({
13676 cls : 'text-danger fa fa-lg fa-star',
13677 tooltip : 'This field is required',
13678 style : 'margin-right:5px;'
13683 if (Roo.bootstrap.version == 3) {
13684 this.el.addClass(this.invalidClass);
13686 this.inputEl().addClass('is-invalid');
13689 // fixme ... this may be depricated need to test..
13690 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13692 var feedback = this.el.select('.form-control-feedback', true).first();
13695 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13697 if(this.getValue().length || this.forceFeedback){
13698 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13705 this.fireEvent('invalid', this, msg);
13713 * trigger field - base class for combo..
13718 * @class Roo.bootstrap.form.TriggerField
13719 * @extends Roo.bootstrap.form.Input
13720 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13721 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13722 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13723 * for which you can provide a custom implementation. For example:
13725 var trigger = new Roo.bootstrap.form.TriggerField();
13726 trigger.onTriggerClick = myTriggerFn;
13727 trigger.applyTo('my-field');
13730 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13731 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13732 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13733 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13734 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13737 * Create a new TriggerField.
13738 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13739 * to the base TextField)
13741 Roo.bootstrap.form.TriggerField = function(config){
13742 this.mimicing = false;
13743 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13746 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13748 * @cfg {String} triggerClass A CSS class to apply to the trigger
13751 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13756 * @cfg {Boolean} removable (true|false) special filter default false
13760 /** @cfg {Boolean} grow @hide */
13761 /** @cfg {Number} growMin @hide */
13762 /** @cfg {Number} growMax @hide */
13768 autoSize: Roo.emptyFn,
13772 deferHeight : true,
13775 actionMode : 'wrap',
13780 getAutoCreate : function(){
13782 var align = this.labelAlign || this.parentLabelAlign();
13787 cls: 'form-group' //input-group
13794 type : this.inputType,
13795 cls : 'form-control',
13796 autocomplete: 'new-password',
13797 placeholder : this.placeholder || ''
13801 input.name = this.name;
13804 input.cls += ' input-' + this.size;
13807 if (this.disabled) {
13808 input.disabled=true;
13811 var inputblock = input;
13813 if(this.hasFeedback && !this.allowBlank){
13817 cls: 'glyphicon form-control-feedback'
13820 if(this.removable && !this.editable ){
13822 cls : 'has-feedback',
13828 cls : 'roo-combo-removable-btn close'
13835 cls : 'has-feedback',
13844 if(this.removable && !this.editable ){
13846 cls : 'roo-removable',
13852 cls : 'roo-combo-removable-btn close'
13859 if (this.before || this.after) {
13862 cls : 'input-group',
13866 inputblock.cn.push({
13868 cls : 'input-group-addon input-group-prepend input-group-text',
13873 inputblock.cn.push(input);
13875 if(this.hasFeedback && !this.allowBlank){
13876 inputblock.cls += ' has-feedback';
13877 inputblock.cn.push(feedback);
13881 inputblock.cn.push({
13883 cls : 'input-group-addon input-group-append input-group-text',
13892 var ibwrap = inputblock;
13897 cls: 'roo-select2-choices',
13901 cls: 'roo-select2-search-field',
13913 cls: 'roo-select2-container input-group',
13918 cls: 'form-hidden-field'
13924 if(!this.multiple && this.showToggleBtn){
13930 if (this.caret != false) {
13933 cls: 'fa fa-' + this.caret
13940 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13942 Roo.bootstrap.version == 3 ? caret : '',
13945 cls: 'combobox-clear',
13959 combobox.cls += ' roo-select2-container-multi';
13963 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13964 tooltip : 'This field is required'
13967 if (this.allowBlank) {
13970 style : 'display:none'
13976 if (align ==='left' && this.fieldLabel.length) {
13978 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13985 cls : 'control-label',
13986 html : this.fieldLabel
13998 var labelCfg = cfg.cn[1];
13999 var contentCfg = cfg.cn[2];
14001 if(this.indicatorpos == 'right'){
14006 cls : 'control-label',
14010 html : this.fieldLabel
14024 labelCfg = cfg.cn[0];
14025 contentCfg = cfg.cn[1];
14028 if(this.labelWidth > 12){
14029 labelCfg.style = "width: " + this.labelWidth + 'px';
14032 if(this.labelWidth < 13 && this.labelmd == 0){
14033 this.labelmd = this.labelWidth;
14036 if(this.labellg > 0){
14037 labelCfg.cls += ' col-lg-' + this.labellg;
14038 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14041 if(this.labelmd > 0){
14042 labelCfg.cls += ' col-md-' + this.labelmd;
14043 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14046 if(this.labelsm > 0){
14047 labelCfg.cls += ' col-sm-' + this.labelsm;
14048 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14051 if(this.labelxs > 0){
14052 labelCfg.cls += ' col-xs-' + this.labelxs;
14053 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14056 } else if ( this.fieldLabel.length) {
14057 // Roo.log(" label");
14062 //cls : 'input-group-addon',
14063 html : this.fieldLabel
14071 if(this.indicatorpos == 'right'){
14079 html : this.fieldLabel
14093 // Roo.log(" no label && no align");
14100 ['xs','sm','md','lg'].map(function(size){
14101 if (settings[size]) {
14102 cfg.cls += ' col-' + size + '-' + settings[size];
14113 onResize : function(w, h){
14114 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14115 // if(typeof w == 'number'){
14116 // var x = w - this.trigger.getWidth();
14117 // this.inputEl().setWidth(this.adjustWidth('input', x));
14118 // this.trigger.setStyle('left', x+'px');
14123 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14126 getResizeEl : function(){
14127 return this.inputEl();
14131 getPositionEl : function(){
14132 return this.inputEl();
14136 alignErrorIcon : function(){
14137 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14141 initEvents : function(){
14145 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14146 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14147 if(!this.multiple && this.showToggleBtn){
14148 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14149 if(this.hideTrigger){
14150 this.trigger.setDisplayed(false);
14152 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14156 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14159 if(this.removable && !this.editable && !this.tickable){
14160 var close = this.closeTriggerEl();
14163 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14164 close.on('click', this.removeBtnClick, this, close);
14168 //this.trigger.addClassOnOver('x-form-trigger-over');
14169 //this.trigger.addClassOnClick('x-form-trigger-click');
14172 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14176 closeTriggerEl : function()
14178 var close = this.el.select('.roo-combo-removable-btn', true).first();
14179 return close ? close : false;
14182 removeBtnClick : function(e, h, el)
14184 e.preventDefault();
14186 if(this.fireEvent("remove", this) !== false){
14188 this.fireEvent("afterremove", this)
14192 createList : function()
14194 this.list = Roo.get(document.body).createChild({
14195 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14196 cls: 'typeahead typeahead-long dropdown-menu shadow',
14197 style: 'display:none'
14200 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14205 initTrigger : function(){
14210 onDestroy : function(){
14212 this.trigger.removeAllListeners();
14213 // this.trigger.remove();
14216 // this.wrap.remove();
14218 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14222 onFocus : function(){
14223 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14225 if(!this.mimicing){
14226 this.wrap.addClass('x-trigger-wrap-focus');
14227 this.mimicing = true;
14228 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14229 if(this.monitorTab){
14230 this.el.on("keydown", this.checkTab, this);
14237 checkTab : function(e){
14238 if(e.getKey() == e.TAB){
14239 this.triggerBlur();
14244 onBlur : function(){
14249 mimicBlur : function(e, t){
14251 if(!this.wrap.contains(t) && this.validateBlur()){
14252 this.triggerBlur();
14258 triggerBlur : function(){
14259 this.mimicing = false;
14260 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14261 if(this.monitorTab){
14262 this.el.un("keydown", this.checkTab, this);
14264 //this.wrap.removeClass('x-trigger-wrap-focus');
14265 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14269 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14270 validateBlur : function(e, t){
14275 onDisable : function(){
14276 this.inputEl().dom.disabled = true;
14277 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14279 // this.wrap.addClass('x-item-disabled');
14284 onEnable : function(){
14285 this.inputEl().dom.disabled = false;
14286 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14288 // this.el.removeClass('x-item-disabled');
14293 onShow : function(){
14294 var ae = this.getActionEl();
14297 ae.dom.style.display = '';
14298 ae.dom.style.visibility = 'visible';
14304 onHide : function(){
14305 var ae = this.getActionEl();
14306 ae.dom.style.display = 'none';
14310 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14311 * by an implementing function.
14313 * @param {EventObject} e
14315 onTriggerClick : Roo.emptyFn
14323 * @class Roo.bootstrap.form.CardUploader
14324 * @extends Roo.bootstrap.Button
14325 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14326 * @cfg {Number} errorTimeout default 3000
14327 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14328 * @cfg {Array} html The button text.
14332 * Create a new CardUploader
14333 * @param {Object} config The config object
14336 Roo.bootstrap.form.CardUploader = function(config){
14340 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14343 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14351 * When a image is clicked on - and needs to display a slideshow or similar..
14352 * @param {Roo.bootstrap.Card} this
14353 * @param {Object} The image information data
14359 * When a the download link is clicked
14360 * @param {Roo.bootstrap.Card} this
14361 * @param {Object} The image information data contains
14368 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14371 errorTimeout : 3000,
14375 fileCollection : false,
14378 getAutoCreate : function()
14382 cls :'form-group' ,
14387 //cls : 'input-group-addon',
14388 html : this.fieldLabel
14396 value : this.value,
14397 cls : 'd-none form-control'
14402 multiple : 'multiple',
14404 cls : 'd-none roo-card-upload-selector'
14408 cls : 'roo-card-uploader-button-container w-100 mb-2'
14411 cls : 'card-columns roo-card-uploader-container'
14421 getChildContainer : function() /// what children are added to.
14423 return this.containerEl;
14426 getButtonContainer : function() /// what children are added to.
14428 return this.el.select(".roo-card-uploader-button-container").first();
14431 initEvents : function()
14434 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14438 xns: Roo.bootstrap,
14441 container_method : 'getButtonContainer' ,
14442 html : this.html, // fix changable?
14445 'click' : function(btn, e) {
14454 this.urlAPI = (window.createObjectURL && window) ||
14455 (window.URL && URL.revokeObjectURL && URL) ||
14456 (window.webkitURL && webkitURL);
14461 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14463 this.selectorEl.on('change', this.onFileSelected, this);
14466 this.images.forEach(function(img) {
14469 this.images = false;
14471 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14477 onClick : function(e)
14479 e.preventDefault();
14481 this.selectorEl.dom.click();
14485 onFileSelected : function(e)
14487 e.preventDefault();
14489 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14493 Roo.each(this.selectorEl.dom.files, function(file){
14494 this.addFile(file);
14503 addFile : function(file)
14506 if(typeof(file) === 'string'){
14507 throw "Add file by name?"; // should not happen
14511 if(!file || !this.urlAPI){
14521 var url = _this.urlAPI.createObjectURL( file);
14524 id : Roo.bootstrap.form.CardUploader.ID--,
14525 is_uploaded : false,
14529 mimetype : file.type,
14537 * addCard - add an Attachment to the uploader
14538 * @param data - the data about the image to upload
14542 title : "Title of file",
14543 is_uploaded : false,
14544 src : "http://.....",
14545 srcfile : { the File upload object },
14546 mimetype : file.type,
14549 .. any other data...
14555 addCard : function (data)
14557 // hidden input element?
14558 // if the file is not an image...
14559 //then we need to use something other that and header_image
14564 xns : Roo.bootstrap,
14565 xtype : 'CardFooter',
14568 xns : Roo.bootstrap,
14574 xns : Roo.bootstrap,
14576 html : String.format("<small>{0}</small>", data.title),
14577 cls : 'col-10 text-left',
14582 click : function() {
14584 t.fireEvent( "download", t, data );
14590 xns : Roo.bootstrap,
14592 style: 'max-height: 28px; ',
14598 click : function() {
14599 t.removeCard(data.id)
14611 var cn = this.addxtype(
14614 xns : Roo.bootstrap,
14617 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14618 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14619 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14624 initEvents : function() {
14625 Roo.bootstrap.Card.prototype.initEvents.call(this);
14627 this.imgEl = this.el.select('.card-img-top').first();
14629 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14630 this.imgEl.set({ 'pointer' : 'cursor' });
14633 this.getCardFooter().addClass('p-1');
14640 // dont' really need ot update items.
14641 // this.items.push(cn);
14642 this.fileCollection.add(cn);
14644 if (!data.srcfile) {
14645 this.updateInput();
14650 var reader = new FileReader();
14651 reader.addEventListener("load", function() {
14652 data.srcdata = reader.result;
14655 reader.readAsDataURL(data.srcfile);
14660 removeCard : function(id)
14663 var card = this.fileCollection.get(id);
14664 card.data.is_deleted = 1;
14665 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14666 //this.fileCollection.remove(card);
14667 //this.items = this.items.filter(function(e) { return e != card });
14668 // dont' really need ot update items.
14669 card.el.dom.parentNode.removeChild(card.el.dom);
14670 this.updateInput();
14676 this.fileCollection.each(function(card) {
14677 if (card.el.dom && card.el.dom.parentNode) {
14678 card.el.dom.parentNode.removeChild(card.el.dom);
14681 this.fileCollection.clear();
14682 this.updateInput();
14685 updateInput : function()
14688 this.fileCollection.each(function(e) {
14692 this.inputEl().dom.value = JSON.stringify(data);
14702 Roo.bootstrap.form.CardUploader.ID = -1;/*
14704 * Ext JS Library 1.1.1
14705 * Copyright(c) 2006-2007, Ext JS, LLC.
14707 * Originally Released Under LGPL - original licence link has changed is not relivant.
14710 * <script type="text/javascript">
14715 * @class Roo.data.SortTypes
14717 * Defines the default sorting (casting?) comparison functions used when sorting data.
14719 Roo.data.SortTypes = {
14721 * Default sort that does nothing
14722 * @param {Mixed} s The value being converted
14723 * @return {Mixed} The comparison value
14725 none : function(s){
14730 * The regular expression used to strip tags
14734 stripTagsRE : /<\/?[^>]+>/gi,
14737 * Strips all HTML tags to sort on text only
14738 * @param {Mixed} s The value being converted
14739 * @return {String} The comparison value
14741 asText : function(s){
14742 return String(s).replace(this.stripTagsRE, "");
14746 * Strips all HTML tags to sort on text only - Case insensitive
14747 * @param {Mixed} s The value being converted
14748 * @return {String} The comparison value
14750 asUCText : function(s){
14751 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14755 * Case insensitive string
14756 * @param {Mixed} s The value being converted
14757 * @return {String} The comparison value
14759 asUCString : function(s) {
14760 return String(s).toUpperCase();
14765 * @param {Mixed} s The value being converted
14766 * @return {Number} The comparison value
14768 asDate : function(s) {
14772 if(s instanceof Date){
14773 return s.getTime();
14775 return Date.parse(String(s));
14780 * @param {Mixed} s The value being converted
14781 * @return {Float} The comparison value
14783 asFloat : function(s) {
14784 var val = parseFloat(String(s).replace(/,/g, ""));
14793 * @param {Mixed} s The value being converted
14794 * @return {Number} The comparison value
14796 asInt : function(s) {
14797 var val = parseInt(String(s).replace(/,/g, ""));
14805 * Ext JS Library 1.1.1
14806 * Copyright(c) 2006-2007, Ext JS, LLC.
14808 * Originally Released Under LGPL - original licence link has changed is not relivant.
14811 * <script type="text/javascript">
14815 * @class Roo.data.Record
14816 * Instances of this class encapsulate both record <em>definition</em> information, and record
14817 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14818 * to access Records cached in an {@link Roo.data.Store} object.<br>
14820 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14821 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14824 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14826 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14827 * {@link #create}. The parameters are the same.
14828 * @param {Array} data An associative Array of data values keyed by the field name.
14829 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14830 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14831 * not specified an integer id is generated.
14833 Roo.data.Record = function(data, id){
14834 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14839 * Generate a constructor for a specific record layout.
14840 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14841 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14842 * Each field definition object may contain the following properties: <ul>
14843 * <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,
14844 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14845 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14846 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14847 * is being used, then this is a string containing the javascript expression to reference the data relative to
14848 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14849 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14850 * this may be omitted.</p></li>
14851 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14852 * <ul><li>auto (Default, implies no conversion)</li>
14857 * <li>date</li></ul></p></li>
14858 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14859 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14860 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14861 * by the Reader into an object that will be stored in the Record. It is passed the
14862 * following parameters:<ul>
14863 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14865 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14867 * <br>usage:<br><pre><code>
14868 var TopicRecord = Roo.data.Record.create(
14869 {name: 'title', mapping: 'topic_title'},
14870 {name: 'author', mapping: 'username'},
14871 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14872 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14873 {name: 'lastPoster', mapping: 'user2'},
14874 {name: 'excerpt', mapping: 'post_text'}
14877 var myNewRecord = new TopicRecord({
14878 title: 'Do my job please',
14881 lastPost: new Date(),
14882 lastPoster: 'Animal',
14883 excerpt: 'No way dude!'
14885 myStore.add(myNewRecord);
14890 Roo.data.Record.create = function(o){
14891 var f = function(){
14892 f.superclass.constructor.apply(this, arguments);
14894 Roo.extend(f, Roo.data.Record);
14895 var p = f.prototype;
14896 p.fields = new Roo.util.MixedCollection(false, function(field){
14899 for(var i = 0, len = o.length; i < len; i++){
14900 p.fields.add(new Roo.data.Field(o[i]));
14902 f.getField = function(name){
14903 return p.fields.get(name);
14908 Roo.data.Record.AUTO_ID = 1000;
14909 Roo.data.Record.EDIT = 'edit';
14910 Roo.data.Record.REJECT = 'reject';
14911 Roo.data.Record.COMMIT = 'commit';
14913 Roo.data.Record.prototype = {
14915 * Readonly flag - true if this record has been modified.
14924 join : function(store){
14925 this.store = store;
14929 * Set the named field to the specified value.
14930 * @param {String} name The name of the field to set.
14931 * @param {Object} value The value to set the field to.
14933 set : function(name, value){
14934 if(this.data[name] == value){
14938 if(!this.modified){
14939 this.modified = {};
14941 if(typeof this.modified[name] == 'undefined'){
14942 this.modified[name] = this.data[name];
14944 this.data[name] = value;
14945 if(!this.editing && this.store){
14946 this.store.afterEdit(this);
14951 * Get the value of the named field.
14952 * @param {String} name The name of the field to get the value of.
14953 * @return {Object} The value of the field.
14955 get : function(name){
14956 return this.data[name];
14960 beginEdit : function(){
14961 this.editing = true;
14962 this.modified = {};
14966 cancelEdit : function(){
14967 this.editing = false;
14968 delete this.modified;
14972 endEdit : function(){
14973 this.editing = false;
14974 if(this.dirty && this.store){
14975 this.store.afterEdit(this);
14980 * Usually called by the {@link Roo.data.Store} which owns the Record.
14981 * Rejects all changes made to the Record since either creation, or the last commit operation.
14982 * Modified fields are reverted to their original values.
14984 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14985 * of reject operations.
14987 reject : function(){
14988 var m = this.modified;
14990 if(typeof m[n] != "function"){
14991 this.data[n] = m[n];
14994 this.dirty = false;
14995 delete this.modified;
14996 this.editing = false;
14998 this.store.afterReject(this);
15003 * Usually called by the {@link Roo.data.Store} which owns the Record.
15004 * Commits all changes made to the Record since either creation, or the last commit operation.
15006 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15007 * of commit operations.
15009 commit : function(){
15010 this.dirty = false;
15011 delete this.modified;
15012 this.editing = false;
15014 this.store.afterCommit(this);
15019 hasError : function(){
15020 return this.error != null;
15024 clearError : function(){
15029 * Creates a copy of this record.
15030 * @param {String} id (optional) A new record id if you don't want to use this record's id
15033 copy : function(newId) {
15034 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15038 * Ext JS Library 1.1.1
15039 * Copyright(c) 2006-2007, Ext JS, LLC.
15041 * Originally Released Under LGPL - original licence link has changed is not relivant.
15044 * <script type="text/javascript">
15050 * @class Roo.data.Store
15051 * @extends Roo.util.Observable
15052 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15053 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15055 * 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
15056 * has no knowledge of the format of the data returned by the Proxy.<br>
15058 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15059 * instances from the data object. These records are cached and made available through accessor functions.
15061 * Creates a new Store.
15062 * @param {Object} config A config object containing the objects needed for the Store to access data,
15063 * and read the data into Records.
15065 Roo.data.Store = function(config){
15066 this.data = new Roo.util.MixedCollection(false);
15067 this.data.getKey = function(o){
15070 this.baseParams = {};
15072 this.paramNames = {
15077 "multisort" : "_multisort"
15080 if(config && config.data){
15081 this.inlineData = config.data;
15082 delete config.data;
15085 Roo.apply(this, config);
15087 if(this.reader){ // reader passed
15088 this.reader = Roo.factory(this.reader, Roo.data);
15089 this.reader.xmodule = this.xmodule || false;
15090 if(!this.recordType){
15091 this.recordType = this.reader.recordType;
15093 if(this.reader.onMetaChange){
15094 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15098 if(this.recordType){
15099 this.fields = this.recordType.prototype.fields;
15101 this.modified = [];
15105 * @event datachanged
15106 * Fires when the data cache has changed, and a widget which is using this Store
15107 * as a Record cache should refresh its view.
15108 * @param {Store} this
15110 datachanged : true,
15112 * @event metachange
15113 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15114 * @param {Store} this
15115 * @param {Object} meta The JSON metadata
15120 * Fires when Records have been added to the Store
15121 * @param {Store} this
15122 * @param {Roo.data.Record[]} records The array of Records added
15123 * @param {Number} index The index at which the record(s) were added
15128 * Fires when a Record has been removed from the Store
15129 * @param {Store} this
15130 * @param {Roo.data.Record} record The Record that was removed
15131 * @param {Number} index The index at which the record was removed
15136 * Fires when a Record has been updated
15137 * @param {Store} this
15138 * @param {Roo.data.Record} record The Record that was updated
15139 * @param {String} operation The update operation being performed. Value may be one of:
15141 Roo.data.Record.EDIT
15142 Roo.data.Record.REJECT
15143 Roo.data.Record.COMMIT
15149 * Fires when the data cache has been cleared.
15150 * @param {Store} this
15154 * @event beforeload
15155 * Fires before a request is made for a new data object. If the beforeload handler returns false
15156 * the load action will be canceled.
15157 * @param {Store} this
15158 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15162 * @event beforeloadadd
15163 * Fires after a new set of Records has been loaded.
15164 * @param {Store} this
15165 * @param {Roo.data.Record[]} records The Records that were loaded
15166 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15168 beforeloadadd : true,
15171 * Fires after a new set of Records has been loaded, before they are added to the store.
15172 * @param {Store} this
15173 * @param {Roo.data.Record[]} records The Records that were loaded
15174 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15175 * @params {Object} return from reader
15179 * @event loadexception
15180 * Fires if an exception occurs in the Proxy during loading.
15181 * Called with the signature of the Proxy's "loadexception" event.
15182 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15185 * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15186 * @param {Object} opts - load Options
15187 * @param {Object} jsonData from your request (normally this contains the Exception)
15189 loadexception : true
15193 this.proxy = Roo.factory(this.proxy, Roo.data);
15194 this.proxy.xmodule = this.xmodule || false;
15195 this.relayEvents(this.proxy, ["loadexception"]);
15197 this.sortToggle = {};
15198 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15200 Roo.data.Store.superclass.constructor.call(this);
15202 if(this.inlineData){
15203 this.loadData(this.inlineData);
15204 delete this.inlineData;
15208 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15210 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15211 * without a remote query - used by combo/forms at present.
15215 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15218 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15221 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15222 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15225 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15226 * on any HTTP request
15229 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15232 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15236 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15237 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15239 remoteSort : false,
15242 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15243 * loaded or when a record is removed. (defaults to false).
15245 pruneModifiedRecords : false,
15248 lastOptions : null,
15251 * Add Records to the Store and fires the add event.
15252 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15254 add : function(records){
15255 records = [].concat(records);
15256 for(var i = 0, len = records.length; i < len; i++){
15257 records[i].join(this);
15259 var index = this.data.length;
15260 this.data.addAll(records);
15261 this.fireEvent("add", this, records, index);
15265 * Remove a Record from the Store and fires the remove event.
15266 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15268 remove : function(record){
15269 var index = this.data.indexOf(record);
15270 this.data.removeAt(index);
15272 if(this.pruneModifiedRecords){
15273 this.modified.remove(record);
15275 this.fireEvent("remove", this, record, index);
15279 * Remove all Records from the Store and fires the clear event.
15281 removeAll : function(){
15283 if(this.pruneModifiedRecords){
15284 this.modified = [];
15286 this.fireEvent("clear", this);
15290 * Inserts Records to the Store at the given index and fires the add event.
15291 * @param {Number} index The start index at which to insert the passed Records.
15292 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15294 insert : function(index, records){
15295 records = [].concat(records);
15296 for(var i = 0, len = records.length; i < len; i++){
15297 this.data.insert(index, records[i]);
15298 records[i].join(this);
15300 this.fireEvent("add", this, records, index);
15304 * Get the index within the cache of the passed Record.
15305 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15306 * @return {Number} The index of the passed Record. Returns -1 if not found.
15308 indexOf : function(record){
15309 return this.data.indexOf(record);
15313 * Get the index within the cache of the Record with the passed id.
15314 * @param {String} id The id of the Record to find.
15315 * @return {Number} The index of the Record. Returns -1 if not found.
15317 indexOfId : function(id){
15318 return this.data.indexOfKey(id);
15322 * Get the Record with the specified id.
15323 * @param {String} id The id of the Record to find.
15324 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15326 getById : function(id){
15327 return this.data.key(id);
15331 * Get the Record at the specified index.
15332 * @param {Number} index The index of the Record to find.
15333 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15335 getAt : function(index){
15336 return this.data.itemAt(index);
15340 * Returns a range of Records between specified indices.
15341 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15342 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15343 * @return {Roo.data.Record[]} An array of Records
15345 getRange : function(start, end){
15346 return this.data.getRange(start, end);
15350 storeOptions : function(o){
15351 o = Roo.apply({}, o);
15354 this.lastOptions = o;
15358 * Loads the Record cache from the configured Proxy using the configured Reader.
15360 * If using remote paging, then the first load call must specify the <em>start</em>
15361 * and <em>limit</em> properties in the options.params property to establish the initial
15362 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15364 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15365 * and this call will return before the new data has been loaded. Perform any post-processing
15366 * in a callback function, or in a "load" event handler.</strong>
15368 * @param {Object} options An object containing properties which control loading options:<ul>
15369 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15370 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15373 data : data, // array of key=>value data like JsonReader
15374 total : data.length,
15380 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15381 * passed the following arguments:<ul>
15382 * <li>r : Roo.data.Record[]</li>
15383 * <li>options: Options object from the load call</li>
15384 * <li>success: Boolean success indicator</li></ul></li>
15385 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15386 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15389 load : function(options){
15390 options = options || {};
15391 if(this.fireEvent("beforeload", this, options) !== false){
15392 this.storeOptions(options);
15393 var p = Roo.apply(options.params || {}, this.baseParams);
15394 // if meta was not loaded from remote source.. try requesting it.
15395 if (!this.reader.metaFromRemote) {
15396 p._requestMeta = 1;
15398 if(this.sortInfo && this.remoteSort){
15399 var pn = this.paramNames;
15400 p[pn["sort"]] = this.sortInfo.field;
15401 p[pn["dir"]] = this.sortInfo.direction;
15403 if (this.multiSort) {
15404 var pn = this.paramNames;
15405 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15408 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15413 * Reloads the Record cache from the configured Proxy using the configured Reader and
15414 * the options from the last load operation performed.
15415 * @param {Object} options (optional) An object containing properties which may override the options
15416 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15417 * the most recently used options are reused).
15419 reload : function(options){
15420 this.load(Roo.applyIf(options||{}, this.lastOptions));
15424 // Called as a callback by the Reader during a load operation.
15425 loadRecords : function(o, options, success){
15428 if(success !== false){
15429 this.fireEvent("load", this, [], options, o);
15431 if(options.callback){
15432 options.callback.call(options.scope || this, [], options, false);
15436 // if data returned failure - throw an exception.
15437 if (o.success === false) {
15438 // show a message if no listener is registered.
15439 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15440 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15442 // loadmask wil be hooked into this..
15443 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15446 var r = o.records, t = o.totalRecords || r.length;
15448 this.fireEvent("beforeloadadd", this, r, options, o);
15450 if(!options || options.add !== true){
15451 if(this.pruneModifiedRecords){
15452 this.modified = [];
15454 for(var i = 0, len = r.length; i < len; i++){
15458 this.data = this.snapshot;
15459 delete this.snapshot;
15462 this.data.addAll(r);
15463 this.totalLength = t;
15465 this.fireEvent("datachanged", this);
15467 this.totalLength = Math.max(t, this.data.length+r.length);
15471 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15473 var e = new Roo.data.Record({});
15475 e.set(this.parent.displayField, this.parent.emptyTitle);
15476 e.set(this.parent.valueField, '');
15481 this.fireEvent("load", this, r, options, o);
15482 if(options.callback){
15483 options.callback.call(options.scope || this, r, options, true);
15489 * Loads data from a passed data block. A Reader which understands the format of the data
15490 * must have been configured in the constructor.
15491 * @param {Object} data The data block from which to read the Records. The format of the data expected
15492 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15493 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15495 loadData : function(o, append){
15496 var r = this.reader.readRecords(o);
15497 this.loadRecords(r, {add: append}, true);
15501 * using 'cn' the nested child reader read the child array into it's child stores.
15502 * @param {Object} rec The record with a 'children array
15504 loadDataFromChildren : function(rec)
15506 this.loadData(this.reader.toLoadData(rec));
15511 * Gets the number of cached records.
15513 * <em>If using paging, this may not be the total size of the dataset. If the data object
15514 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15515 * the data set size</em>
15517 getCount : function(){
15518 return this.data.length || 0;
15522 * Gets the total number of records in the dataset as returned by the server.
15524 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15525 * the dataset size</em>
15527 getTotalCount : function(){
15528 return this.totalLength || 0;
15532 * Returns the sort state of the Store as an object with two properties:
15534 field {String} The name of the field by which the Records are sorted
15535 direction {String} The sort order, "ASC" or "DESC"
15538 getSortState : function(){
15539 return this.sortInfo;
15543 applySort : function(){
15544 if(this.sortInfo && !this.remoteSort){
15545 var s = this.sortInfo, f = s.field;
15546 var st = this.fields.get(f).sortType;
15547 var fn = function(r1, r2){
15548 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15549 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15551 this.data.sort(s.direction, fn);
15552 if(this.snapshot && this.snapshot != this.data){
15553 this.snapshot.sort(s.direction, fn);
15559 * Sets the default sort column and order to be used by the next load operation.
15560 * @param {String} fieldName The name of the field to sort by.
15561 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15563 setDefaultSort : function(field, dir){
15564 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15568 * Sort the Records.
15569 * If remote sorting is used, the sort is performed on the server, and the cache is
15570 * reloaded. If local sorting is used, the cache is sorted internally.
15571 * @param {String} fieldName The name of the field to sort by.
15572 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15574 sort : function(fieldName, dir){
15575 var f = this.fields.get(fieldName);
15577 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15579 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15580 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15585 this.sortToggle[f.name] = dir;
15586 this.sortInfo = {field: f.name, direction: dir};
15587 if(!this.remoteSort){
15589 this.fireEvent("datachanged", this);
15591 this.load(this.lastOptions);
15596 * Calls the specified function for each of the Records in the cache.
15597 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15598 * Returning <em>false</em> aborts and exits the iteration.
15599 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15601 each : function(fn, scope){
15602 this.data.each(fn, scope);
15606 * Gets all records modified since the last commit. Modified records are persisted across load operations
15607 * (e.g., during paging).
15608 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15610 getModifiedRecords : function(){
15611 return this.modified;
15615 createFilterFn : function(property, value, anyMatch){
15616 if(!value.exec){ // not a regex
15617 value = String(value);
15618 if(value.length == 0){
15621 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15623 return function(r){
15624 return value.test(r.data[property]);
15629 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15630 * @param {String} property A field on your records
15631 * @param {Number} start The record index to start at (defaults to 0)
15632 * @param {Number} end The last record index to include (defaults to length - 1)
15633 * @return {Number} The sum
15635 sum : function(property, start, end){
15636 var rs = this.data.items, v = 0;
15637 start = start || 0;
15638 end = (end || end === 0) ? end : rs.length-1;
15640 for(var i = start; i <= end; i++){
15641 v += (rs[i].data[property] || 0);
15647 * Filter the records by a specified property.
15648 * @param {String} field A field on your records
15649 * @param {String/RegExp} value Either a string that the field
15650 * should start with or a RegExp to test against the field
15651 * @param {Boolean} anyMatch True to match any part not just the beginning
15653 filter : function(property, value, anyMatch){
15654 var fn = this.createFilterFn(property, value, anyMatch);
15655 return fn ? this.filterBy(fn) : this.clearFilter();
15659 * Filter by a function. The specified function will be called with each
15660 * record in this data source. If the function returns true the record is included,
15661 * otherwise it is filtered.
15662 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15663 * @param {Object} scope (optional) The scope of the function (defaults to this)
15665 filterBy : function(fn, scope){
15666 this.snapshot = this.snapshot || this.data;
15667 this.data = this.queryBy(fn, scope||this);
15668 this.fireEvent("datachanged", this);
15672 * Query the records by a specified property.
15673 * @param {String} field A field on your records
15674 * @param {String/RegExp} value Either a string that the field
15675 * should start with or a RegExp to test against the field
15676 * @param {Boolean} anyMatch True to match any part not just the beginning
15677 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15679 query : function(property, value, anyMatch){
15680 var fn = this.createFilterFn(property, value, anyMatch);
15681 return fn ? this.queryBy(fn) : this.data.clone();
15685 * Query by a function. The specified function will be called with each
15686 * record in this data source. If the function returns true the record is included
15688 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15689 * @param {Object} scope (optional) The scope of the function (defaults to this)
15690 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15692 queryBy : function(fn, scope){
15693 var data = this.snapshot || this.data;
15694 return data.filterBy(fn, scope||this);
15698 * Collects unique values for a particular dataIndex from this store.
15699 * @param {String} dataIndex The property to collect
15700 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15701 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15702 * @return {Array} An array of the unique values
15704 collect : function(dataIndex, allowNull, bypassFilter){
15705 var d = (bypassFilter === true && this.snapshot) ?
15706 this.snapshot.items : this.data.items;
15707 var v, sv, r = [], l = {};
15708 for(var i = 0, len = d.length; i < len; i++){
15709 v = d[i].data[dataIndex];
15711 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15720 * Revert to a view of the Record cache with no filtering applied.
15721 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15723 clearFilter : function(suppressEvent){
15724 if(this.snapshot && this.snapshot != this.data){
15725 this.data = this.snapshot;
15726 delete this.snapshot;
15727 if(suppressEvent !== true){
15728 this.fireEvent("datachanged", this);
15734 afterEdit : function(record){
15735 if(this.modified.indexOf(record) == -1){
15736 this.modified.push(record);
15738 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15742 afterReject : function(record){
15743 this.modified.remove(record);
15744 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15748 afterCommit : function(record){
15749 this.modified.remove(record);
15750 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15754 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15755 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15757 commitChanges : function(){
15758 var m = this.modified.slice(0);
15759 this.modified = [];
15760 for(var i = 0, len = m.length; i < len; i++){
15766 * Cancel outstanding changes on all changed records.
15768 rejectChanges : function(){
15769 var m = this.modified.slice(0);
15770 this.modified = [];
15771 for(var i = 0, len = m.length; i < len; i++){
15776 onMetaChange : function(meta, rtype, o){
15777 this.recordType = rtype;
15778 this.fields = rtype.prototype.fields;
15779 delete this.snapshot;
15780 this.sortInfo = meta.sortInfo || this.sortInfo;
15781 this.modified = [];
15782 this.fireEvent('metachange', this, this.reader.meta);
15785 moveIndex : function(data, type)
15787 var index = this.indexOf(data);
15789 var newIndex = index + type;
15793 this.insert(newIndex, data);
15798 * Ext JS Library 1.1.1
15799 * Copyright(c) 2006-2007, Ext JS, LLC.
15801 * Originally Released Under LGPL - original licence link has changed is not relivant.
15804 * <script type="text/javascript">
15808 * @class Roo.data.SimpleStore
15809 * @extends Roo.data.Store
15810 * Small helper class to make creating Stores from Array data easier.
15811 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15812 * @cfg {Array} fields An array of field definition objects, or field name strings.
15813 * @cfg {Object} an existing reader (eg. copied from another store)
15814 * @cfg {Array} data The multi-dimensional array of data
15815 * @cfg {Roo.data.DataProxy} proxy [not-required]
15816 * @cfg {Roo.data.Reader} reader [not-required]
15818 * @param {Object} config
15820 Roo.data.SimpleStore = function(config)
15822 Roo.data.SimpleStore.superclass.constructor.call(this, {
15824 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15827 Roo.data.Record.create(config.fields)
15829 proxy : new Roo.data.MemoryProxy(config.data)
15833 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15835 * Ext JS Library 1.1.1
15836 * Copyright(c) 2006-2007, Ext JS, LLC.
15838 * Originally Released Under LGPL - original licence link has changed is not relivant.
15841 * <script type="text/javascript">
15846 * @extends Roo.data.Store
15847 * @class Roo.data.JsonStore
15848 * Small helper class to make creating Stores for JSON data easier. <br/>
15850 var store = new Roo.data.JsonStore({
15851 url: 'get-images.php',
15853 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15856 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15857 * JsonReader and HttpProxy (unless inline data is provided).</b>
15858 * @cfg {Array} fields An array of field definition objects, or field name strings.
15860 * @param {Object} config
15862 Roo.data.JsonStore = function(c){
15863 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15864 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15865 reader: new Roo.data.JsonReader(c, c.fields)
15868 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15870 * Ext JS Library 1.1.1
15871 * Copyright(c) 2006-2007, Ext JS, LLC.
15873 * Originally Released Under LGPL - original licence link has changed is not relivant.
15876 * <script type="text/javascript">
15880 Roo.data.Field = function(config){
15881 if(typeof config == "string"){
15882 config = {name: config};
15884 Roo.apply(this, config);
15887 this.type = "auto";
15890 var st = Roo.data.SortTypes;
15891 // named sortTypes are supported, here we look them up
15892 if(typeof this.sortType == "string"){
15893 this.sortType = st[this.sortType];
15896 // set default sortType for strings and dates
15897 if(!this.sortType){
15900 this.sortType = st.asUCString;
15903 this.sortType = st.asDate;
15906 this.sortType = st.none;
15911 var stripRe = /[\$,%]/g;
15913 // prebuilt conversion function for this field, instead of
15914 // switching every time we're reading a value
15916 var cv, dateFormat = this.dateFormat;
15921 cv = function(v){ return v; };
15924 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15928 return v !== undefined && v !== null && v !== '' ?
15929 parseInt(String(v).replace(stripRe, ""), 10) : '';
15934 return v !== undefined && v !== null && v !== '' ?
15935 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15940 cv = function(v){ return v === true || v === "true" || v == 1; };
15947 if(v instanceof Date){
15951 if(dateFormat == "timestamp"){
15952 return new Date(v*1000);
15954 return Date.parseDate(v, dateFormat);
15956 var parsed = Date.parse(v);
15957 return parsed ? new Date(parsed) : null;
15966 Roo.data.Field.prototype = {
15974 * Ext JS Library 1.1.1
15975 * Copyright(c) 2006-2007, Ext JS, LLC.
15977 * Originally Released Under LGPL - original licence link has changed is not relivant.
15980 * <script type="text/javascript">
15983 // Base class for reading structured data from a data source. This class is intended to be
15984 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15987 * @class Roo.data.DataReader
15989 * Base class for reading structured data from a data source. This class is intended to be
15990 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15993 Roo.data.DataReader = function(meta, recordType){
15997 this.recordType = recordType instanceof Array ?
15998 Roo.data.Record.create(recordType) : recordType;
16001 Roo.data.DataReader.prototype = {
16004 readerType : 'Data',
16006 * Create an empty record
16007 * @param {Object} data (optional) - overlay some values
16008 * @return {Roo.data.Record} record created.
16010 newRow : function(d) {
16012 this.recordType.prototype.fields.each(function(c) {
16014 case 'int' : da[c.name] = 0; break;
16015 case 'date' : da[c.name] = new Date(); break;
16016 case 'float' : da[c.name] = 0.0; break;
16017 case 'boolean' : da[c.name] = false; break;
16018 default : da[c.name] = ""; break;
16022 return new this.recordType(Roo.apply(da, d));
16028 * Ext JS Library 1.1.1
16029 * Copyright(c) 2006-2007, Ext JS, LLC.
16031 * Originally Released Under LGPL - original licence link has changed is not relivant.
16034 * <script type="text/javascript">
16038 * @class Roo.data.DataProxy
16039 * @extends Roo.util.Observable
16041 * This class is an abstract base class for implementations which provide retrieval of
16042 * unformatted data objects.<br>
16044 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16045 * (of the appropriate type which knows how to parse the data object) to provide a block of
16046 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16048 * Custom implementations must implement the load method as described in
16049 * {@link Roo.data.HttpProxy#load}.
16051 Roo.data.DataProxy = function(){
16054 * @event beforeload
16055 * Fires before a network request is made to retrieve a data object.
16056 * @param {Object} This DataProxy object.
16057 * @param {Object} params The params parameter to the load function.
16062 * Fires before the load method's callback is called.
16063 * @param {Object} This DataProxy object.
16064 * @param {Object} o The data object.
16065 * @param {Object} arg The callback argument object passed to the load function.
16069 * @event loadexception
16070 * Fires if an Exception occurs during data retrieval.
16071 * @param {Object} This DataProxy object.
16072 * @param {Object} o The data object.
16073 * @param {Object} arg The callback argument object passed to the load function.
16074 * @param {Object} e The Exception.
16076 loadexception : true
16078 Roo.data.DataProxy.superclass.constructor.call(this);
16081 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16084 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16088 * Ext JS Library 1.1.1
16089 * Copyright(c) 2006-2007, Ext JS, LLC.
16091 * Originally Released Under LGPL - original licence link has changed is not relivant.
16094 * <script type="text/javascript">
16097 * @class Roo.data.MemoryProxy
16098 * @extends Roo.data.DataProxy
16099 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16100 * to the Reader when its load method is called.
16102 * @param {Object} config A config object containing the objects needed for the Store to access data,
16104 Roo.data.MemoryProxy = function(config){
16106 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16107 data = config.data;
16109 Roo.data.MemoryProxy.superclass.constructor.call(this);
16113 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16116 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16119 * Load data from the requested source (in this case an in-memory
16120 * data object passed to the constructor), read the data object into
16121 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16122 * process that block using the passed callback.
16123 * @param {Object} params This parameter is not used by the MemoryProxy class.
16124 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16125 * object into a block of Roo.data.Records.
16126 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16127 * The function must be passed <ul>
16128 * <li>The Record block object</li>
16129 * <li>The "arg" argument from the load function</li>
16130 * <li>A boolean success indicator</li>
16132 * @param {Object} scope The scope in which to call the callback
16133 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16135 load : function(params, reader, callback, scope, arg){
16136 params = params || {};
16139 result = reader.readRecords(params.data ? params.data :this.data);
16141 this.fireEvent("loadexception", this, arg, null, e);
16142 callback.call(scope, null, arg, false);
16145 callback.call(scope, result, arg, true);
16149 update : function(params, records){
16154 * Ext JS Library 1.1.1
16155 * Copyright(c) 2006-2007, Ext JS, LLC.
16157 * Originally Released Under LGPL - original licence link has changed is not relivant.
16160 * <script type="text/javascript">
16163 * @class Roo.data.HttpProxy
16164 * @extends Roo.data.DataProxy
16165 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16166 * configured to reference a certain URL.<br><br>
16168 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16169 * from which the running page was served.<br><br>
16171 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16173 * Be aware that to enable the browser to parse an XML document, the server must set
16174 * the Content-Type header in the HTTP response to "text/xml".
16176 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16177 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16178 * will be used to make the request.
16180 Roo.data.HttpProxy = function(conn){
16181 Roo.data.HttpProxy.superclass.constructor.call(this);
16182 // is conn a conn config or a real conn?
16184 this.useAjax = !conn || !conn.events;
16188 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16189 // thse are take from connection...
16192 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
16195 * @cfg {Object} extraParams An object containing properties which are used as
16196 * extra parameters to each request made by this object. (defaults to undefined)
16199 * @cfg {Object} defaultHeaders An object containing request headers which are added
16200 * to each request made by this object. (defaults to undefined)
16203 * @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)
16206 * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16209 * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16215 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16219 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16220 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16221 * a finer-grained basis than the DataProxy events.
16223 getConnection : function(){
16224 return this.useAjax ? Roo.Ajax : this.conn;
16228 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16229 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16230 * process that block using the passed callback.
16231 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16232 * for the request to the remote server.
16233 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16234 * object into a block of Roo.data.Records.
16235 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16236 * The function must be passed <ul>
16237 * <li>The Record block object</li>
16238 * <li>The "arg" argument from the load function</li>
16239 * <li>A boolean success indicator</li>
16241 * @param {Object} scope The scope in which to call the callback
16242 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16244 load : function(params, reader, callback, scope, arg){
16245 if(this.fireEvent("beforeload", this, params) !== false){
16247 params : params || {},
16249 callback : callback,
16254 callback : this.loadResponse,
16258 Roo.applyIf(o, this.conn);
16259 if(this.activeRequest){
16260 Roo.Ajax.abort(this.activeRequest);
16262 this.activeRequest = Roo.Ajax.request(o);
16264 this.conn.request(o);
16267 callback.call(scope||this, null, arg, false);
16272 loadResponse : function(o, success, response){
16273 delete this.activeRequest;
16275 this.fireEvent("loadexception", this, o, response);
16276 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16281 result = o.reader.read(response);
16284 o.raw = { errorMsg : response.responseText };
16285 this.fireEvent("loadexception", this, o, response, e);
16286 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16290 this.fireEvent("load", this, o, o.request.arg);
16291 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16295 update : function(dataSet){
16300 updateResponse : function(dataSet){
16305 * Ext JS Library 1.1.1
16306 * Copyright(c) 2006-2007, Ext JS, LLC.
16308 * Originally Released Under LGPL - original licence link has changed is not relivant.
16311 * <script type="text/javascript">
16315 * @class Roo.data.ScriptTagProxy
16316 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16317 * other than the originating domain of the running page.<br><br>
16319 * <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
16320 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16322 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16323 * source code that is used as the source inside a <script> tag.<br><br>
16325 * In order for the browser to process the returned data, the server must wrap the data object
16326 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16327 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16328 * depending on whether the callback name was passed:
16331 boolean scriptTag = false;
16332 String cb = request.getParameter("callback");
16335 response.setContentType("text/javascript");
16337 response.setContentType("application/x-json");
16339 Writer out = response.getWriter();
16341 out.write(cb + "(");
16343 out.print(dataBlock.toJsonString());
16350 * @param {Object} config A configuration object.
16352 Roo.data.ScriptTagProxy = function(config){
16353 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16354 Roo.apply(this, config);
16355 this.head = document.getElementsByTagName("head")[0];
16358 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16360 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16362 * @cfg {String} url The URL from which to request the data object.
16365 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16369 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16370 * the server the name of the callback function set up by the load call to process the returned data object.
16371 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16372 * javascript output which calls this named function passing the data object as its only parameter.
16374 callbackParam : "callback",
16376 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16377 * name to the request.
16382 * Load data from the configured URL, read the data object into
16383 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16384 * process that block using the passed callback.
16385 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16386 * for the request to the remote server.
16387 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16388 * object into a block of Roo.data.Records.
16389 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16390 * The function must be passed <ul>
16391 * <li>The Record block object</li>
16392 * <li>The "arg" argument from the load function</li>
16393 * <li>A boolean success indicator</li>
16395 * @param {Object} scope The scope in which to call the callback
16396 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16398 load : function(params, reader, callback, scope, arg){
16399 if(this.fireEvent("beforeload", this, params) !== false){
16401 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16403 var url = this.url;
16404 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16406 url += "&_dc=" + (new Date().getTime());
16408 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16411 cb : "stcCallback"+transId,
16412 scriptId : "stcScript"+transId,
16416 callback : callback,
16422 window[trans.cb] = function(o){
16423 conn.handleResponse(o, trans);
16426 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16428 if(this.autoAbort !== false){
16432 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16434 var script = document.createElement("script");
16435 script.setAttribute("src", url);
16436 script.setAttribute("type", "text/javascript");
16437 script.setAttribute("id", trans.scriptId);
16438 this.head.appendChild(script);
16440 this.trans = trans;
16442 callback.call(scope||this, null, arg, false);
16447 isLoading : function(){
16448 return this.trans ? true : false;
16452 * Abort the current server request.
16454 abort : function(){
16455 if(this.isLoading()){
16456 this.destroyTrans(this.trans);
16461 destroyTrans : function(trans, isLoaded){
16462 this.head.removeChild(document.getElementById(trans.scriptId));
16463 clearTimeout(trans.timeoutId);
16465 window[trans.cb] = undefined;
16467 delete window[trans.cb];
16470 // if hasn't been loaded, wait for load to remove it to prevent script error
16471 window[trans.cb] = function(){
16472 window[trans.cb] = undefined;
16474 delete window[trans.cb];
16481 handleResponse : function(o, trans){
16482 this.trans = false;
16483 this.destroyTrans(trans, true);
16486 result = trans.reader.readRecords(o);
16488 this.fireEvent("loadexception", this, o, trans.arg, e);
16489 trans.callback.call(trans.scope||window, null, trans.arg, false);
16492 this.fireEvent("load", this, o, trans.arg);
16493 trans.callback.call(trans.scope||window, result, trans.arg, true);
16497 handleFailure : function(trans){
16498 this.trans = false;
16499 this.destroyTrans(trans, false);
16500 this.fireEvent("loadexception", this, null, trans.arg);
16501 trans.callback.call(trans.scope||window, null, trans.arg, false);
16505 * Ext JS Library 1.1.1
16506 * Copyright(c) 2006-2007, Ext JS, LLC.
16508 * Originally Released Under LGPL - original licence link has changed is not relivant.
16511 * <script type="text/javascript">
16515 * @class Roo.data.JsonReader
16516 * @extends Roo.data.DataReader
16517 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16518 * based on mappings in a provided Roo.data.Record constructor.
16520 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16521 * in the reply previously.
16526 var RecordDef = Roo.data.Record.create([
16527 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16528 {name: 'occupation'} // This field will use "occupation" as the mapping.
16530 var myReader = new Roo.data.JsonReader({
16531 totalProperty: "results", // The property which contains the total dataset size (optional)
16532 root: "rows", // The property which contains an Array of row objects
16533 id: "id" // The property within each row object that provides an ID for the record (optional)
16537 * This would consume a JSON file like this:
16539 { 'results': 2, 'rows': [
16540 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16541 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16544 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16545 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16546 * paged from the remote server.
16547 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16548 * @cfg {String} root name of the property which contains the Array of row objects.
16549 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16550 * @cfg {Array} fields Array of field definition objects
16552 * Create a new JsonReader
16553 * @param {Object} meta Metadata configuration options
16554 * @param {Object} recordType Either an Array of field definition objects,
16555 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16557 Roo.data.JsonReader = function(meta, recordType){
16560 // set some defaults:
16561 Roo.applyIf(meta, {
16562 totalProperty: 'total',
16563 successProperty : 'success',
16568 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16570 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16572 readerType : 'Json',
16575 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16576 * Used by Store query builder to append _requestMeta to params.
16579 metaFromRemote : false,
16581 * This method is only used by a DataProxy which has retrieved data from a remote server.
16582 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16583 * @return {Object} data A data block which is used by an Roo.data.Store object as
16584 * a cache of Roo.data.Records.
16586 read : function(response){
16587 var json = response.responseText;
16589 var o = /* eval:var:o */ eval("("+json+")");
16591 throw {message: "JsonReader.read: Json object not found"};
16597 this.metaFromRemote = true;
16598 this.meta = o.metaData;
16599 this.recordType = Roo.data.Record.create(o.metaData.fields);
16600 this.onMetaChange(this.meta, this.recordType, o);
16602 return this.readRecords(o);
16605 // private function a store will implement
16606 onMetaChange : function(meta, recordType, o){
16613 simpleAccess: function(obj, subsc) {
16620 getJsonAccessor: function(){
16622 return function(expr) {
16624 return(re.test(expr))
16625 ? new Function("obj", "return obj." + expr)
16630 return Roo.emptyFn;
16635 * Create a data block containing Roo.data.Records from an XML document.
16636 * @param {Object} o An object which contains an Array of row objects in the property specified
16637 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16638 * which contains the total size of the dataset.
16639 * @return {Object} data A data block which is used by an Roo.data.Store object as
16640 * a cache of Roo.data.Records.
16642 readRecords : function(o){
16644 * After any data loads, the raw JSON data is available for further custom processing.
16648 var s = this.meta, Record = this.recordType,
16649 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16651 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16653 if(s.totalProperty) {
16654 this.getTotal = this.getJsonAccessor(s.totalProperty);
16656 if(s.successProperty) {
16657 this.getSuccess = this.getJsonAccessor(s.successProperty);
16659 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16661 var g = this.getJsonAccessor(s.id);
16662 this.getId = function(rec) {
16664 return (r === undefined || r === "") ? null : r;
16667 this.getId = function(){return null;};
16670 for(var jj = 0; jj < fl; jj++){
16672 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16673 this.ef[jj] = this.getJsonAccessor(map);
16677 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16678 if(s.totalProperty){
16679 var vt = parseInt(this.getTotal(o), 10);
16684 if(s.successProperty){
16685 var vs = this.getSuccess(o);
16686 if(vs === false || vs === 'false'){
16691 for(var i = 0; i < c; i++){
16694 var id = this.getId(n);
16695 for(var j = 0; j < fl; j++){
16697 var v = this.ef[j](n);
16699 Roo.log('missing convert for ' + f.name);
16703 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16707 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16713 var record = new Record(values, id);
16715 records[i] = record;
16721 totalRecords : totalRecords
16724 // used when loading children.. @see loadDataFromChildren
16725 toLoadData: function(rec)
16727 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16728 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16729 return { data : data, total : data.length };
16734 * Ext JS Library 1.1.1
16735 * Copyright(c) 2006-2007, Ext JS, LLC.
16737 * Originally Released Under LGPL - original licence link has changed is not relivant.
16740 * <script type="text/javascript">
16744 * @class Roo.data.ArrayReader
16745 * @extends Roo.data.DataReader
16746 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16747 * Each element of that Array represents a row of data fields. The
16748 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16749 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16753 var RecordDef = Roo.data.Record.create([
16754 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16755 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16757 var myReader = new Roo.data.ArrayReader({
16758 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16762 * This would consume an Array like this:
16764 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16768 * Create a new JsonReader
16769 * @param {Object} meta Metadata configuration options.
16770 * @param {Object|Array} recordType Either an Array of field definition objects
16772 * @cfg {Array} fields Array of field definition objects
16773 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16774 * as specified to {@link Roo.data.Record#create},
16775 * or an {@link Roo.data.Record} object
16778 * created using {@link Roo.data.Record#create}.
16780 Roo.data.ArrayReader = function(meta, recordType)
16782 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16785 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16788 * Create a data block containing Roo.data.Records from an XML document.
16789 * @param {Object} o An Array of row objects which represents the dataset.
16790 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16791 * a cache of Roo.data.Records.
16793 readRecords : function(o)
16795 var sid = this.meta ? this.meta.id : null;
16796 var recordType = this.recordType, fields = recordType.prototype.fields;
16799 for(var i = 0; i < root.length; i++){
16802 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16803 for(var j = 0, jlen = fields.length; j < jlen; j++){
16804 var f = fields.items[j];
16805 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16806 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16808 values[f.name] = v;
16810 var record = new recordType(values, id);
16812 records[records.length] = record;
16816 totalRecords : records.length
16819 // used when loading children.. @see loadDataFromChildren
16820 toLoadData: function(rec)
16822 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16823 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16834 * @class Roo.bootstrap.form.ComboBox
16835 * @extends Roo.bootstrap.form.TriggerField
16836 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16837 * @cfg {Boolean} append (true|false) default false
16838 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16839 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16840 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16841 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16842 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16843 * @cfg {Boolean} animate default true
16844 * @cfg {Boolean} emptyResultText only for touch device
16845 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16846 * @cfg {String} emptyTitle default ''
16847 * @cfg {Number} width fixed with? experimental
16849 * Create a new ComboBox.
16850 * @param {Object} config Configuration options
16852 Roo.bootstrap.form.ComboBox = function(config){
16853 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16857 * Fires when the dropdown list is expanded
16858 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863 * Fires when the dropdown list is collapsed
16864 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16868 * @event beforeselect
16869 * Fires before a list item is selected. Return false to cancel the selection.
16870 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871 * @param {Roo.data.Record} record The data record returned from the underlying store
16872 * @param {Number} index The index of the selected item in the dropdown list
16874 'beforeselect' : true,
16877 * Fires when a list item is selected
16878 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16879 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16880 * @param {Number} index The index of the selected item in the dropdown list
16884 * @event beforequery
16885 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16886 * The event object passed has these properties:
16887 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16888 * @param {String} query The query
16889 * @param {Boolean} forceAll true to force "all" query
16890 * @param {Boolean} cancel true to cancel the query
16891 * @param {Object} e The query event object
16893 'beforequery': true,
16896 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16897 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16903 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16909 * Fires when the remove value from the combobox array
16910 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16914 * @event afterremove
16915 * Fires when the remove value from the combobox array
16916 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16918 'afterremove' : true,
16920 * @event specialfilter
16921 * Fires when specialfilter
16922 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16924 'specialfilter' : true,
16927 * Fires when tick the element
16928 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16932 * @event touchviewdisplay
16933 * Fires when touch view require special display (default is using displayField)
16934 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16935 * @param {Object} cfg set html .
16937 'touchviewdisplay' : true
16942 this.tickItems = [];
16944 this.selectedIndex = -1;
16945 if(this.mode == 'local'){
16946 if(config.queryDelay === undefined){
16947 this.queryDelay = 10;
16949 if(config.minChars === undefined){
16955 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16958 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16959 * rendering into an Roo.Editor, defaults to false)
16962 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16963 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16966 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16969 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16970 * the dropdown list (defaults to undefined, with no header element)
16974 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16978 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16980 listWidth: undefined,
16982 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16983 * mode = 'remote' or 'text' if mode = 'local')
16985 displayField: undefined,
16988 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16989 * mode = 'remote' or 'value' if mode = 'local').
16990 * Note: use of a valueField requires the user make a selection
16991 * in order for a value to be mapped.
16993 valueField: undefined,
16995 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17000 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17001 * field's data value (defaults to the underlying DOM element's name)
17003 hiddenName: undefined,
17005 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17009 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17011 selectedClass: 'active',
17014 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17018 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17019 * anchor positions (defaults to 'tl-bl')
17021 listAlign: 'tl-bl?',
17023 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17027 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17028 * query specified by the allQuery config option (defaults to 'query')
17030 triggerAction: 'query',
17032 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17033 * (defaults to 4, does not apply if editable = false)
17037 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17038 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17042 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17043 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17047 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17048 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17052 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17053 * when editable = true (defaults to false)
17055 selectOnFocus:false,
17057 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17059 queryParam: 'query',
17061 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17062 * when mode = 'remote' (defaults to 'Loading...')
17064 loadingText: 'Loading...',
17066 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17070 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17074 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17075 * traditional select (defaults to true)
17079 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17083 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17087 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17088 * listWidth has a higher value)
17092 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17093 * allow the user to set arbitrary text into the field (defaults to false)
17095 forceSelection:false,
17097 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17098 * if typeAhead = true (defaults to 250)
17100 typeAheadDelay : 250,
17102 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17103 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17105 valueNotFoundText : undefined,
17107 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17109 blockFocus : false,
17112 * @cfg {Boolean} disableClear Disable showing of clear button.
17114 disableClear : false,
17116 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17118 alwaysQuery : false,
17121 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17126 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17128 invalidClass : "has-warning",
17131 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17133 validClass : "has-success",
17136 * @cfg {Boolean} specialFilter (true|false) special filter default false
17138 specialFilter : false,
17141 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17143 mobileTouchView : true,
17146 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17148 useNativeIOS : false,
17151 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17153 mobile_restrict_height : false,
17155 ios_options : false,
17167 btnPosition : 'right',
17168 triggerList : true,
17169 showToggleBtn : true,
17171 emptyResultText: 'Empty',
17172 triggerText : 'Select',
17176 // element that contains real text value.. (when hidden is used..)
17178 getAutoCreate : function()
17183 * Render classic select for iso
17186 if(Roo.isIOS && this.useNativeIOS){
17187 cfg = this.getAutoCreateNativeIOS();
17195 if(Roo.isTouch && this.mobileTouchView){
17196 cfg = this.getAutoCreateTouchView();
17203 if(!this.tickable){
17204 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17209 * ComboBox with tickable selections
17212 var align = this.labelAlign || this.parentLabelAlign();
17215 cls : 'form-group roo-combobox-tickable' //input-group
17218 var btn_text_select = '';
17219 var btn_text_done = '';
17220 var btn_text_cancel = '';
17222 if (this.btn_text_show) {
17223 btn_text_select = 'Select';
17224 btn_text_done = 'Done';
17225 btn_text_cancel = 'Cancel';
17230 cls : 'tickable-buttons',
17235 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17236 //html : this.triggerText
17237 html: btn_text_select
17243 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17245 html: btn_text_done
17251 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17253 html: btn_text_cancel
17259 buttons.cn.unshift({
17261 cls: 'roo-select2-search-field-input'
17267 Roo.each(buttons.cn, function(c){
17269 c.cls += ' btn-' + _this.size;
17272 if (_this.disabled) {
17279 style : 'display: contents',
17284 cls: 'form-hidden-field'
17288 cls: 'roo-select2-choices',
17292 cls: 'roo-select2-search-field',
17303 cls: 'roo-select2-container input-group roo-select2-container-multi',
17309 // cls: 'typeahead typeahead-long dropdown-menu',
17310 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17315 if(this.hasFeedback && !this.allowBlank){
17319 cls: 'glyphicon form-control-feedback'
17322 combobox.cn.push(feedback);
17329 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17330 tooltip : 'This field is required'
17333 if (this.allowBlank) {
17336 style : 'display:none'
17339 if (align ==='left' && this.fieldLabel.length) {
17341 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17348 cls : 'control-label col-form-label',
17349 html : this.fieldLabel
17361 var labelCfg = cfg.cn[1];
17362 var contentCfg = cfg.cn[2];
17365 if(this.indicatorpos == 'right'){
17371 cls : 'control-label col-form-label',
17375 html : this.fieldLabel
17391 labelCfg = cfg.cn[0];
17392 contentCfg = cfg.cn[1];
17396 if(this.labelWidth > 12){
17397 labelCfg.style = "width: " + this.labelWidth + 'px';
17399 if(this.width * 1 > 0){
17400 contentCfg.style = "width: " + this.width + 'px';
17402 if(this.labelWidth < 13 && this.labelmd == 0){
17403 this.labelmd = this.labelWidth;
17406 if(this.labellg > 0){
17407 labelCfg.cls += ' col-lg-' + this.labellg;
17408 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17411 if(this.labelmd > 0){
17412 labelCfg.cls += ' col-md-' + this.labelmd;
17413 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17416 if(this.labelsm > 0){
17417 labelCfg.cls += ' col-sm-' + this.labelsm;
17418 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17421 if(this.labelxs > 0){
17422 labelCfg.cls += ' col-xs-' + this.labelxs;
17423 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17427 } else if ( this.fieldLabel.length) {
17428 // Roo.log(" label");
17433 //cls : 'input-group-addon',
17434 html : this.fieldLabel
17439 if(this.indicatorpos == 'right'){
17443 //cls : 'input-group-addon',
17444 html : this.fieldLabel
17454 // Roo.log(" no label && no align");
17461 ['xs','sm','md','lg'].map(function(size){
17462 if (settings[size]) {
17463 cfg.cls += ' col-' + size + '-' + settings[size];
17471 _initEventsCalled : false,
17474 initEvents: function()
17476 if (this._initEventsCalled) { // as we call render... prevent looping...
17479 this._initEventsCalled = true;
17482 throw "can not find store for combo";
17485 this.indicator = this.indicatorEl();
17487 this.store = Roo.factory(this.store, Roo.data);
17488 this.store.parent = this;
17490 // if we are building from html. then this element is so complex, that we can not really
17491 // use the rendered HTML.
17492 // so we have to trash and replace the previous code.
17493 if (Roo.XComponent.build_from_html) {
17494 // remove this element....
17495 var e = this.el.dom, k=0;
17496 while (e ) { e = e.previousSibling; ++k;}
17501 this.rendered = false;
17503 this.render(this.parent().getChildContainer(true), k);
17506 if(Roo.isIOS && this.useNativeIOS){
17507 this.initIOSView();
17515 if(Roo.isTouch && this.mobileTouchView){
17516 this.initTouchView();
17521 this.initTickableEvents();
17525 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17527 if(this.hiddenName){
17529 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17531 this.hiddenField.dom.value =
17532 this.hiddenValue !== undefined ? this.hiddenValue :
17533 this.value !== undefined ? this.value : '';
17535 // prevent input submission
17536 this.el.dom.removeAttribute('name');
17537 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17542 // this.el.dom.setAttribute('autocomplete', 'off');
17545 var cls = 'x-combo-list';
17547 //this.list = new Roo.Layer({
17548 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17554 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17555 _this.list.setWidth(lw);
17558 this.list.on('mouseover', this.onViewOver, this);
17559 this.list.on('mousemove', this.onViewMove, this);
17560 this.list.on('scroll', this.onViewScroll, this);
17563 this.list.swallowEvent('mousewheel');
17564 this.assetHeight = 0;
17567 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17568 this.assetHeight += this.header.getHeight();
17571 this.innerList = this.list.createChild({cls:cls+'-inner'});
17572 this.innerList.on('mouseover', this.onViewOver, this);
17573 this.innerList.on('mousemove', this.onViewMove, this);
17574 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17576 if(this.allowBlank && !this.pageSize && !this.disableClear){
17577 this.footer = this.list.createChild({cls:cls+'-ft'});
17578 this.pageTb = new Roo.Toolbar(this.footer);
17582 this.footer = this.list.createChild({cls:cls+'-ft'});
17583 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17584 {pageSize: this.pageSize});
17588 if (this.pageTb && this.allowBlank && !this.disableClear) {
17590 this.pageTb.add(new Roo.Toolbar.Fill(), {
17591 cls: 'x-btn-icon x-btn-clear',
17593 handler: function()
17596 _this.clearValue();
17597 _this.onSelect(false, -1);
17602 this.assetHeight += this.footer.getHeight();
17607 this.tpl = Roo.bootstrap.version == 4 ?
17608 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17609 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17612 this.view = new Roo.View(this.list, this.tpl, {
17613 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17615 //this.view.wrapEl.setDisplayed(false);
17616 this.view.on('click', this.onViewClick, this);
17619 this.store.on('beforeload', this.onBeforeLoad, this);
17620 this.store.on('load', this.onLoad, this);
17621 this.store.on('loadexception', this.onLoadException, this);
17623 if(this.resizable){
17624 this.resizer = new Roo.Resizable(this.list, {
17625 pinned:true, handles:'se'
17627 this.resizer.on('resize', function(r, w, h){
17628 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17629 this.listWidth = w;
17630 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17631 this.restrictHeight();
17633 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17636 if(!this.editable){
17637 this.editable = true;
17638 this.setEditable(false);
17643 if (typeof(this.events.add.listeners) != 'undefined') {
17645 this.addicon = this.wrap.createChild(
17646 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17648 this.addicon.on('click', function(e) {
17649 this.fireEvent('add', this);
17652 if (typeof(this.events.edit.listeners) != 'undefined') {
17654 this.editicon = this.wrap.createChild(
17655 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17656 if (this.addicon) {
17657 this.editicon.setStyle('margin-left', '40px');
17659 this.editicon.on('click', function(e) {
17661 // we fire even if inothing is selected..
17662 this.fireEvent('edit', this, this.lastData );
17668 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17669 "up" : function(e){
17670 this.inKeyMode = true;
17674 "down" : function(e){
17675 if(!this.isExpanded()){
17676 this.onTriggerClick();
17678 this.inKeyMode = true;
17683 "enter" : function(e){
17684 // this.onViewClick();
17688 if(this.fireEvent("specialkey", this, e)){
17689 this.onViewClick(false);
17695 "esc" : function(e){
17699 "tab" : function(e){
17702 if(this.fireEvent("specialkey", this, e)){
17703 this.onViewClick(false);
17711 doRelay : function(foo, bar, hname){
17712 if(hname == 'down' || this.scope.isExpanded()){
17713 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17722 this.queryDelay = Math.max(this.queryDelay || 10,
17723 this.mode == 'local' ? 10 : 250);
17726 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17728 if(this.typeAhead){
17729 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17731 if(this.editable !== false){
17732 this.inputEl().on("keyup", this.onKeyUp, this);
17734 if(this.forceSelection){
17735 this.inputEl().on('blur', this.doForce, this);
17739 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17740 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17744 initTickableEvents: function()
17748 if(this.hiddenName){
17750 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17752 this.hiddenField.dom.value =
17753 this.hiddenValue !== undefined ? this.hiddenValue :
17754 this.value !== undefined ? this.value : '';
17756 // prevent input submission
17757 this.el.dom.removeAttribute('name');
17758 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17763 // this.list = this.el.select('ul.dropdown-menu',true).first();
17765 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17766 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17767 if(this.triggerList){
17768 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17771 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17772 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17774 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17775 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17777 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17778 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17780 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17781 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17782 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17785 this.cancelBtn.hide();
17790 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17791 _this.list.setWidth(lw);
17794 this.list.on('mouseover', this.onViewOver, this);
17795 this.list.on('mousemove', this.onViewMove, this);
17797 this.list.on('scroll', this.onViewScroll, this);
17800 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17801 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17804 this.view = new Roo.View(this.list, this.tpl, {
17809 selectedClass: this.selectedClass
17812 //this.view.wrapEl.setDisplayed(false);
17813 this.view.on('click', this.onViewClick, this);
17817 this.store.on('beforeload', this.onBeforeLoad, this);
17818 this.store.on('load', this.onLoad, this);
17819 this.store.on('loadexception', this.onLoadException, this);
17822 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17823 "up" : function(e){
17824 this.inKeyMode = true;
17828 "down" : function(e){
17829 this.inKeyMode = true;
17833 "enter" : function(e){
17834 if(this.fireEvent("specialkey", this, e)){
17835 this.onViewClick(false);
17841 "esc" : function(e){
17842 this.onTickableFooterButtonClick(e, false, false);
17845 "tab" : function(e){
17846 this.fireEvent("specialkey", this, e);
17848 this.onTickableFooterButtonClick(e, false, false);
17855 doRelay : function(e, fn, key){
17856 if(this.scope.isExpanded()){
17857 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17866 this.queryDelay = Math.max(this.queryDelay || 10,
17867 this.mode == 'local' ? 10 : 250);
17870 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17872 if(this.typeAhead){
17873 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17876 if(this.editable !== false){
17877 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17880 this.indicator = this.indicatorEl();
17882 if(this.indicator){
17883 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17884 this.indicator.hide();
17889 onDestroy : function(){
17891 this.view.setStore(null);
17892 this.view.el.removeAllListeners();
17893 this.view.el.remove();
17894 this.view.purgeListeners();
17897 this.list.dom.innerHTML = '';
17901 this.store.un('beforeload', this.onBeforeLoad, this);
17902 this.store.un('load', this.onLoad, this);
17903 this.store.un('loadexception', this.onLoadException, this);
17905 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17909 fireKey : function(e){
17910 if(e.isNavKeyPress() && !this.list.isVisible()){
17911 this.fireEvent("specialkey", this, e);
17916 onResize: function(w, h)
17920 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17922 // if(typeof w != 'number'){
17923 // // we do not handle it!?!?
17926 // var tw = this.trigger.getWidth();
17927 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17928 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17930 // this.inputEl().setWidth( this.adjustWidth('input', x));
17932 // //this.trigger.setStyle('left', x+'px');
17934 // if(this.list && this.listWidth === undefined){
17935 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17936 // this.list.setWidth(lw);
17937 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17945 * Allow or prevent the user from directly editing the field text. If false is passed,
17946 * the user will only be able to select from the items defined in the dropdown list. This method
17947 * is the runtime equivalent of setting the 'editable' config option at config time.
17948 * @param {Boolean} value True to allow the user to directly edit the field text
17950 setEditable : function(value){
17951 if(value == this.editable){
17954 this.editable = value;
17956 this.inputEl().dom.setAttribute('readOnly', true);
17957 this.inputEl().on('mousedown', this.onTriggerClick, this);
17958 this.inputEl().addClass('x-combo-noedit');
17960 this.inputEl().dom.removeAttribute('readOnly');
17961 this.inputEl().un('mousedown', this.onTriggerClick, this);
17962 this.inputEl().removeClass('x-combo-noedit');
17968 onBeforeLoad : function(combo,opts){
17969 if(!this.hasFocus){
17973 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17975 this.restrictHeight();
17976 this.selectedIndex = -1;
17980 onLoad : function(){
17982 this.hasQuery = false;
17984 if(!this.hasFocus){
17988 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17989 this.loading.hide();
17992 if(this.store.getCount() > 0){
17995 this.restrictHeight();
17996 if(this.lastQuery == this.allQuery){
17997 if(this.editable && !this.tickable){
17998 this.inputEl().dom.select();
18002 !this.selectByValue(this.value, true) &&
18005 !this.store.lastOptions ||
18006 typeof(this.store.lastOptions.add) == 'undefined' ||
18007 this.store.lastOptions.add != true
18010 this.select(0, true);
18013 if(this.autoFocus){
18016 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18017 this.taTask.delay(this.typeAheadDelay);
18021 this.onEmptyResults();
18027 onLoadException : function()
18029 this.hasQuery = false;
18031 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18032 this.loading.hide();
18035 if(this.tickable && this.editable){
18040 // only causes errors at present
18041 //Roo.log(this.store.reader.jsonData);
18042 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18044 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18050 onTypeAhead : function(){
18051 if(this.store.getCount() > 0){
18052 var r = this.store.getAt(0);
18053 var newValue = r.data[this.displayField];
18054 var len = newValue.length;
18055 var selStart = this.getRawValue().length;
18057 if(selStart != len){
18058 this.setRawValue(newValue);
18059 this.selectText(selStart, newValue.length);
18065 onSelect : function(record, index){
18067 if(this.fireEvent('beforeselect', this, record, index) !== false){
18069 this.setFromData(index > -1 ? record.data : false);
18072 this.fireEvent('select', this, record, index);
18077 * Returns the currently selected field value or empty string if no value is set.
18078 * @return {String} value The selected value
18080 getValue : function()
18082 if(Roo.isIOS && this.useNativeIOS){
18083 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18087 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18090 if(this.valueField){
18091 return typeof this.value != 'undefined' ? this.value : '';
18093 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18097 getRawValue : function()
18099 if(Roo.isIOS && this.useNativeIOS){
18100 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18103 var v = this.inputEl().getValue();
18109 * Clears any text/value currently set in the field
18111 clearValue : function(){
18113 if(this.hiddenField){
18114 this.hiddenField.dom.value = '';
18117 this.setRawValue('');
18118 this.lastSelectionText = '';
18119 this.lastData = false;
18121 var close = this.closeTriggerEl();
18132 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18133 * will be displayed in the field. If the value does not match the data value of an existing item,
18134 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18135 * Otherwise the field will be blank (although the value will still be set).
18136 * @param {String} value The value to match
18138 setValue : function(v)
18140 if(Roo.isIOS && this.useNativeIOS){
18141 this.setIOSValue(v);
18151 if(this.valueField){
18152 var r = this.findRecord(this.valueField, v);
18154 text = r.data[this.displayField];
18155 }else if(this.valueNotFoundText !== undefined){
18156 text = this.valueNotFoundText;
18159 this.lastSelectionText = text;
18160 if(this.hiddenField){
18161 this.hiddenField.dom.value = v;
18163 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18166 var close = this.closeTriggerEl();
18169 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18175 * @property {Object} the last set data for the element
18180 * Sets the value of the field based on a object which is related to the record format for the store.
18181 * @param {Object} value the value to set as. or false on reset?
18183 setFromData : function(o){
18190 var dv = ''; // display value
18191 var vv = ''; // value value..
18193 if (this.displayField) {
18194 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18196 // this is an error condition!!!
18197 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18200 if(this.valueField){
18201 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18204 var close = this.closeTriggerEl();
18207 if(dv.length || vv * 1 > 0){
18209 this.blockFocus=true;
18215 if(this.hiddenField){
18216 this.hiddenField.dom.value = vv;
18218 this.lastSelectionText = dv;
18219 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18223 // no hidden field.. - we store the value in 'value', but still display
18224 // display field!!!!
18225 this.lastSelectionText = dv;
18226 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18233 reset : function(){
18234 // overridden so that last data is reset..
18241 this.setValue(this.originalValue);
18242 //this.clearInvalid();
18243 this.lastData = false;
18245 this.view.clearSelections();
18251 findRecord : function(prop, value){
18253 if(this.store.getCount() > 0){
18254 this.store.each(function(r){
18255 if(r.data[prop] == value){
18265 getName: function()
18267 // returns hidden if it's set..
18268 if (!this.rendered) {return ''};
18269 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18273 onViewMove : function(e, t){
18274 this.inKeyMode = false;
18278 onViewOver : function(e, t){
18279 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18282 var item = this.view.findItemFromChild(t);
18285 var index = this.view.indexOf(item);
18286 this.select(index, false);
18291 onViewClick : function(view, doFocus, el, e)
18293 var index = this.view.getSelectedIndexes()[0];
18295 var r = this.store.getAt(index);
18299 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18306 Roo.each(this.tickItems, function(v,k){
18308 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18310 _this.tickItems.splice(k, 1);
18312 if(typeof(e) == 'undefined' && view == false){
18313 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18325 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18326 this.tickItems.push(r.data);
18329 if(typeof(e) == 'undefined' && view == false){
18330 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18337 this.onSelect(r, index);
18339 if(doFocus !== false && !this.blockFocus){
18340 this.inputEl().focus();
18345 restrictHeight : function(){
18346 //this.innerList.dom.style.height = '';
18347 //var inner = this.innerList.dom;
18348 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18349 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18350 //this.list.beginUpdate();
18351 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18352 this.list.alignTo(this.inputEl(), this.listAlign);
18353 this.list.alignTo(this.inputEl(), this.listAlign);
18354 //this.list.endUpdate();
18358 onEmptyResults : function(){
18360 if(this.tickable && this.editable){
18361 this.hasFocus = false;
18362 this.restrictHeight();
18370 * Returns true if the dropdown list is expanded, else false.
18372 isExpanded : function(){
18373 return this.list.isVisible();
18377 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18378 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18379 * @param {String} value The data value of the item to select
18380 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18381 * selected item if it is not currently in view (defaults to true)
18382 * @return {Boolean} True if the value matched an item in the list, else false
18384 selectByValue : function(v, scrollIntoView){
18385 if(v !== undefined && v !== null){
18386 var r = this.findRecord(this.valueField || this.displayField, v);
18388 this.select(this.store.indexOf(r), scrollIntoView);
18396 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18397 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18398 * @param {Number} index The zero-based index of the list item to select
18399 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18400 * selected item if it is not currently in view (defaults to true)
18402 select : function(index, scrollIntoView){
18403 this.selectedIndex = index;
18404 this.view.select(index);
18405 if(scrollIntoView !== false){
18406 var el = this.view.getNode(index);
18408 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18411 this.list.scrollChildIntoView(el, false);
18417 selectNext : function(){
18418 var ct = this.store.getCount();
18420 if(this.selectedIndex == -1){
18422 }else if(this.selectedIndex < ct-1){
18423 this.select(this.selectedIndex+1);
18429 selectPrev : function(){
18430 var ct = this.store.getCount();
18432 if(this.selectedIndex == -1){
18434 }else if(this.selectedIndex != 0){
18435 this.select(this.selectedIndex-1);
18441 onKeyUp : function(e){
18442 if(this.editable !== false && !e.isSpecialKey()){
18443 this.lastKey = e.getKey();
18444 this.dqTask.delay(this.queryDelay);
18449 validateBlur : function(){
18450 return !this.list || !this.list.isVisible();
18454 initQuery : function(){
18456 var v = this.getRawValue();
18458 if(this.tickable && this.editable){
18459 v = this.tickableInputEl().getValue();
18466 doForce : function(){
18467 if(this.inputEl().dom.value.length > 0){
18468 this.inputEl().dom.value =
18469 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18475 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18476 * query allowing the query action to be canceled if needed.
18477 * @param {String} query The SQL query to execute
18478 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18479 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18480 * saved in the current store (defaults to false)
18482 doQuery : function(q, forceAll){
18484 if(q === undefined || q === null){
18489 forceAll: forceAll,
18493 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18498 forceAll = qe.forceAll;
18499 if(forceAll === true || (q.length >= this.minChars)){
18501 this.hasQuery = true;
18503 if(this.lastQuery != q || this.alwaysQuery){
18504 this.lastQuery = q;
18505 if(this.mode == 'local'){
18506 this.selectedIndex = -1;
18508 this.store.clearFilter();
18511 if(this.specialFilter){
18512 this.fireEvent('specialfilter', this);
18517 this.store.filter(this.displayField, q);
18520 this.store.fireEvent("datachanged", this.store);
18527 this.store.baseParams[this.queryParam] = q;
18529 var options = {params : this.getParams(q)};
18532 options.add = true;
18533 options.params.start = this.page * this.pageSize;
18536 this.store.load(options);
18539 * this code will make the page width larger, at the beginning, the list not align correctly,
18540 * we should expand the list on onLoad
18541 * so command out it
18546 this.selectedIndex = -1;
18551 this.loadNext = false;
18555 getParams : function(q){
18557 //p[this.queryParam] = q;
18561 p.limit = this.pageSize;
18567 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18569 collapse : function(){
18570 if(!this.isExpanded()){
18576 this.hasFocus = false;
18580 this.cancelBtn.hide();
18581 this.trigger.show();
18584 this.tickableInputEl().dom.value = '';
18585 this.tickableInputEl().blur();
18590 Roo.get(document).un('mousedown', this.collapseIf, this);
18591 Roo.get(document).un('mousewheel', this.collapseIf, this);
18592 if (!this.editable) {
18593 Roo.get(document).un('keydown', this.listKeyPress, this);
18595 this.fireEvent('collapse', this);
18601 collapseIf : function(e){
18602 var in_combo = e.within(this.el);
18603 var in_list = e.within(this.list);
18604 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18606 if (in_combo || in_list || is_list) {
18607 //e.stopPropagation();
18612 this.onTickableFooterButtonClick(e, false, false);
18620 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18622 expand : function(){
18624 if(this.isExpanded() || !this.hasFocus){
18628 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18629 this.list.setWidth(lw);
18635 this.restrictHeight();
18639 this.tickItems = Roo.apply([], this.item);
18642 this.cancelBtn.show();
18643 this.trigger.hide();
18646 this.tickableInputEl().focus();
18651 Roo.get(document).on('mousedown', this.collapseIf, this);
18652 Roo.get(document).on('mousewheel', this.collapseIf, this);
18653 if (!this.editable) {
18654 Roo.get(document).on('keydown', this.listKeyPress, this);
18657 this.fireEvent('expand', this);
18661 // Implements the default empty TriggerField.onTriggerClick function
18662 onTriggerClick : function(e)
18664 Roo.log('trigger click');
18666 if(this.disabled || !this.triggerList){
18671 this.loadNext = false;
18673 if(this.isExpanded()){
18675 if (!this.blockFocus) {
18676 this.inputEl().focus();
18680 this.hasFocus = true;
18681 if(this.triggerAction == 'all') {
18682 this.doQuery(this.allQuery, true);
18684 this.doQuery(this.getRawValue());
18686 if (!this.blockFocus) {
18687 this.inputEl().focus();
18692 onTickableTriggerClick : function(e)
18699 this.loadNext = false;
18700 this.hasFocus = true;
18702 if(this.triggerAction == 'all') {
18703 this.doQuery(this.allQuery, true);
18705 this.doQuery(this.getRawValue());
18709 onSearchFieldClick : function(e)
18711 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18712 this.onTickableFooterButtonClick(e, false, false);
18716 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18721 this.loadNext = false;
18722 this.hasFocus = true;
18724 if(this.triggerAction == 'all') {
18725 this.doQuery(this.allQuery, true);
18727 this.doQuery(this.getRawValue());
18731 listKeyPress : function(e)
18733 //Roo.log('listkeypress');
18734 // scroll to first matching element based on key pres..
18735 if (e.isSpecialKey()) {
18738 var k = String.fromCharCode(e.getKey()).toUpperCase();
18741 var csel = this.view.getSelectedNodes();
18742 var cselitem = false;
18744 var ix = this.view.indexOf(csel[0]);
18745 cselitem = this.store.getAt(ix);
18746 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18752 this.store.each(function(v) {
18754 // start at existing selection.
18755 if (cselitem.id == v.id) {
18761 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18762 match = this.store.indexOf(v);
18768 if (match === false) {
18769 return true; // no more action?
18772 this.view.select(match);
18773 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18774 sn.scrollIntoView(sn.dom.parentNode, false);
18777 onViewScroll : function(e, t){
18779 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){
18783 this.hasQuery = true;
18785 this.loading = this.list.select('.loading', true).first();
18787 if(this.loading === null){
18788 this.list.createChild({
18790 cls: 'loading roo-select2-more-results roo-select2-active',
18791 html: 'Loading more results...'
18794 this.loading = this.list.select('.loading', true).first();
18796 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18798 this.loading.hide();
18801 this.loading.show();
18806 this.loadNext = true;
18808 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18813 addItem : function(o)
18815 var dv = ''; // display value
18817 if (this.displayField) {
18818 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18820 // this is an error condition!!!
18821 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18828 var choice = this.choices.createChild({
18830 cls: 'roo-select2-search-choice',
18839 cls: 'roo-select2-search-choice-close fa fa-times',
18844 }, this.searchField);
18846 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18848 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18856 this.inputEl().dom.value = '';
18861 onRemoveItem : function(e, _self, o)
18863 e.preventDefault();
18865 this.lastItem = Roo.apply([], this.item);
18867 var index = this.item.indexOf(o.data) * 1;
18870 Roo.log('not this item?!');
18874 this.item.splice(index, 1);
18879 this.fireEvent('remove', this, e);
18885 syncValue : function()
18887 if(!this.item.length){
18894 Roo.each(this.item, function(i){
18895 if(_this.valueField){
18896 value.push(i[_this.valueField]);
18903 this.value = value.join(',');
18905 if(this.hiddenField){
18906 this.hiddenField.dom.value = this.value;
18909 this.store.fireEvent("datachanged", this.store);
18914 clearItem : function()
18916 if(!this.multiple){
18922 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18930 if(this.tickable && !Roo.isTouch){
18931 this.view.refresh();
18935 inputEl: function ()
18937 if(Roo.isIOS && this.useNativeIOS){
18938 return this.el.select('select.roo-ios-select', true).first();
18941 if(Roo.isTouch && this.mobileTouchView){
18942 return this.el.select('input.form-control',true).first();
18946 return this.searchField;
18949 return this.el.select('input.form-control',true).first();
18952 onTickableFooterButtonClick : function(e, btn, el)
18954 e.preventDefault();
18956 this.lastItem = Roo.apply([], this.item);
18958 if(btn && btn.name == 'cancel'){
18959 this.tickItems = Roo.apply([], this.item);
18968 Roo.each(this.tickItems, function(o){
18976 validate : function()
18978 if(this.getVisibilityEl().hasClass('hidden')){
18982 var v = this.getRawValue();
18985 v = this.getValue();
18988 if(this.disabled || this.allowBlank || v.length){
18993 this.markInvalid();
18997 tickableInputEl : function()
18999 if(!this.tickable || !this.editable){
19000 return this.inputEl();
19003 return this.inputEl().select('.roo-select2-search-field-input', true).first();
19007 getAutoCreateTouchView : function()
19012 cls: 'form-group' //input-group
19018 type : this.inputType,
19019 cls : 'form-control x-combo-noedit',
19020 autocomplete: 'new-password',
19021 placeholder : this.placeholder || '',
19026 input.name = this.name;
19030 input.cls += ' input-' + this.size;
19033 if (this.disabled) {
19034 input.disabled = true;
19038 cls : 'roo-combobox-wrap',
19045 inputblock.cls += ' input-group';
19047 inputblock.cn.unshift({
19049 cls : 'input-group-addon input-group-prepend input-group-text',
19054 if(this.removable && !this.multiple){
19055 inputblock.cls += ' roo-removable';
19057 inputblock.cn.push({
19060 cls : 'roo-combo-removable-btn close'
19064 if(this.hasFeedback && !this.allowBlank){
19066 inputblock.cls += ' has-feedback';
19068 inputblock.cn.push({
19070 cls: 'glyphicon form-control-feedback'
19077 inputblock.cls += (this.before) ? '' : ' input-group';
19079 inputblock.cn.push({
19081 cls : 'input-group-addon input-group-append input-group-text',
19087 var ibwrap = inputblock;
19092 cls: 'roo-select2-choices',
19096 cls: 'roo-select2-search-field',
19109 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19114 cls: 'form-hidden-field'
19120 if(!this.multiple && this.showToggleBtn){
19126 if (this.caret != false) {
19129 cls: 'fa fa-' + this.caret
19136 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19138 Roo.bootstrap.version == 3 ? caret : '',
19141 cls: 'combobox-clear',
19155 combobox.cls += ' roo-select2-container-multi';
19158 var required = this.allowBlank ? {
19160 style: 'display: none'
19163 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19164 tooltip : 'This field is required'
19167 var align = this.labelAlign || this.parentLabelAlign();
19169 if (align ==='left' && this.fieldLabel.length) {
19175 cls : 'control-label col-form-label',
19176 html : this.fieldLabel
19180 cls : 'roo-combobox-wrap ',
19187 var labelCfg = cfg.cn[1];
19188 var contentCfg = cfg.cn[2];
19191 if(this.indicatorpos == 'right'){
19196 cls : 'control-label col-form-label',
19200 html : this.fieldLabel
19206 cls : "roo-combobox-wrap ",
19214 labelCfg = cfg.cn[0];
19215 contentCfg = cfg.cn[1];
19220 if(this.labelWidth > 12){
19221 labelCfg.style = "width: " + this.labelWidth + 'px';
19224 if(this.labelWidth < 13 && this.labelmd == 0){
19225 this.labelmd = this.labelWidth;
19228 if(this.labellg > 0){
19229 labelCfg.cls += ' col-lg-' + this.labellg;
19230 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19233 if(this.labelmd > 0){
19234 labelCfg.cls += ' col-md-' + this.labelmd;
19235 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19238 if(this.labelsm > 0){
19239 labelCfg.cls += ' col-sm-' + this.labelsm;
19240 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19243 if(this.labelxs > 0){
19244 labelCfg.cls += ' col-xs-' + this.labelxs;
19245 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19249 } else if ( this.fieldLabel.length) {
19254 cls : 'control-label',
19255 html : this.fieldLabel
19266 if(this.indicatorpos == 'right'){
19270 cls : 'control-label',
19271 html : this.fieldLabel,
19289 var settings = this;
19291 ['xs','sm','md','lg'].map(function(size){
19292 if (settings[size]) {
19293 cfg.cls += ' col-' + size + '-' + settings[size];
19300 initTouchView : function()
19302 this.renderTouchView();
19304 this.touchViewEl.on('scroll', function(){
19305 this.el.dom.scrollTop = 0;
19308 this.originalValue = this.getValue();
19310 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19312 this.inputEl().on("click", this.showTouchView, this);
19313 if (this.triggerEl) {
19314 this.triggerEl.on("click", this.showTouchView, this);
19318 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19319 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19321 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19323 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19324 this.store.on('load', this.onTouchViewLoad, this);
19325 this.store.on('loadexception', this.onTouchViewLoadException, this);
19327 if(this.hiddenName){
19329 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19331 this.hiddenField.dom.value =
19332 this.hiddenValue !== undefined ? this.hiddenValue :
19333 this.value !== undefined ? this.value : '';
19335 this.el.dom.removeAttribute('name');
19336 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19340 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19341 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19344 if(this.removable && !this.multiple){
19345 var close = this.closeTriggerEl();
19347 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19348 close.on('click', this.removeBtnClick, this, close);
19352 * fix the bug in Safari iOS8
19354 this.inputEl().on("focus", function(e){
19355 document.activeElement.blur();
19358 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19365 renderTouchView : function()
19367 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19368 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19370 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19371 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19373 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19374 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19375 this.touchViewBodyEl.setStyle('overflow', 'auto');
19377 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19378 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19380 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19381 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19385 showTouchView : function()
19391 this.touchViewHeaderEl.hide();
19393 if(this.modalTitle.length){
19394 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19395 this.touchViewHeaderEl.show();
19398 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19399 this.touchViewEl.show();
19401 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19403 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19404 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19406 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19408 if(this.modalTitle.length){
19409 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19412 this.touchViewBodyEl.setHeight(bodyHeight);
19416 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19418 this.touchViewEl.addClass(['in','show']);
19421 if(this._touchViewMask){
19422 Roo.get(document.body).addClass("x-body-masked");
19423 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19424 this._touchViewMask.setStyle('z-index', 10000);
19425 this._touchViewMask.addClass('show');
19428 this.doTouchViewQuery();
19432 hideTouchView : function()
19434 this.touchViewEl.removeClass(['in','show']);
19438 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19440 this.touchViewEl.setStyle('display', 'none');
19443 if(this._touchViewMask){
19444 this._touchViewMask.removeClass('show');
19445 Roo.get(document.body).removeClass("x-body-masked");
19449 setTouchViewValue : function()
19456 Roo.each(this.tickItems, function(o){
19461 this.hideTouchView();
19464 doTouchViewQuery : function()
19473 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19477 if(!this.alwaysQuery || this.mode == 'local'){
19478 this.onTouchViewLoad();
19485 onTouchViewBeforeLoad : function(combo,opts)
19491 onTouchViewLoad : function()
19493 if(this.store.getCount() < 1){
19494 this.onTouchViewEmptyResults();
19498 this.clearTouchView();
19500 var rawValue = this.getRawValue();
19502 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19504 this.tickItems = [];
19506 this.store.data.each(function(d, rowIndex){
19507 var row = this.touchViewListGroup.createChild(template);
19509 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19510 row.addClass(d.data.cls);
19513 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19516 html : d.data[this.displayField]
19519 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19520 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19523 row.removeClass('selected');
19524 if(!this.multiple && this.valueField &&
19525 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19528 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19529 row.addClass('selected');
19532 if(this.multiple && this.valueField &&
19533 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19537 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19538 this.tickItems.push(d.data);
19541 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19545 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19547 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19549 if(this.modalTitle.length){
19550 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19553 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19555 if(this.mobile_restrict_height && listHeight < bodyHeight){
19556 this.touchViewBodyEl.setHeight(listHeight);
19561 if(firstChecked && listHeight > bodyHeight){
19562 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19567 onTouchViewLoadException : function()
19569 this.hideTouchView();
19572 onTouchViewEmptyResults : function()
19574 this.clearTouchView();
19576 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19578 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19582 clearTouchView : function()
19584 this.touchViewListGroup.dom.innerHTML = '';
19587 onTouchViewClick : function(e, el, o)
19589 e.preventDefault();
19592 var rowIndex = o.rowIndex;
19594 var r = this.store.getAt(rowIndex);
19596 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19598 if(!this.multiple){
19599 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19600 c.dom.removeAttribute('checked');
19603 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19605 this.setFromData(r.data);
19607 var close = this.closeTriggerEl();
19613 this.hideTouchView();
19615 this.fireEvent('select', this, r, rowIndex);
19620 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19621 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19622 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19626 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19627 this.addItem(r.data);
19628 this.tickItems.push(r.data);
19632 getAutoCreateNativeIOS : function()
19635 cls: 'form-group' //input-group,
19640 cls : 'roo-ios-select'
19644 combobox.name = this.name;
19647 if (this.disabled) {
19648 combobox.disabled = true;
19651 var settings = this;
19653 ['xs','sm','md','lg'].map(function(size){
19654 if (settings[size]) {
19655 cfg.cls += ' col-' + size + '-' + settings[size];
19665 initIOSView : function()
19667 this.store.on('load', this.onIOSViewLoad, this);
19672 onIOSViewLoad : function()
19674 if(this.store.getCount() < 1){
19678 this.clearIOSView();
19680 if(this.allowBlank) {
19682 var default_text = '-- SELECT --';
19684 if(this.placeholder.length){
19685 default_text = this.placeholder;
19688 if(this.emptyTitle.length){
19689 default_text += ' - ' + this.emptyTitle + ' -';
19692 var opt = this.inputEl().createChild({
19695 html : default_text
19699 o[this.valueField] = 0;
19700 o[this.displayField] = default_text;
19702 this.ios_options.push({
19709 this.store.data.each(function(d, rowIndex){
19713 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19714 html = d.data[this.displayField];
19719 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19720 value = d.data[this.valueField];
19729 if(this.value == d.data[this.valueField]){
19730 option['selected'] = true;
19733 var opt = this.inputEl().createChild(option);
19735 this.ios_options.push({
19742 this.inputEl().on('change', function(){
19743 this.fireEvent('select', this);
19748 clearIOSView: function()
19750 this.inputEl().dom.innerHTML = '';
19752 this.ios_options = [];
19755 setIOSValue: function(v)
19759 if(!this.ios_options){
19763 Roo.each(this.ios_options, function(opts){
19765 opts.el.dom.removeAttribute('selected');
19767 if(opts.data[this.valueField] != v){
19771 opts.el.dom.setAttribute('selected', true);
19777 * @cfg {Boolean} grow
19781 * @cfg {Number} growMin
19785 * @cfg {Number} growMax
19794 Roo.apply(Roo.bootstrap.form.ComboBox, {
19798 cls: 'modal-header',
19820 cls: 'list-group-item',
19824 cls: 'roo-combobox-list-group-item-value'
19828 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19842 listItemCheckbox : {
19844 cls: 'list-group-item',
19848 cls: 'roo-combobox-list-group-item-value'
19852 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19868 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19873 cls: 'modal-footer',
19881 cls: 'col-xs-6 text-left',
19884 cls: 'btn btn-danger roo-touch-view-cancel',
19890 cls: 'col-xs-6 text-right',
19893 cls: 'btn btn-success roo-touch-view-ok',
19904 Roo.apply(Roo.bootstrap.form.ComboBox, {
19906 touchViewTemplate : {
19908 cls: 'modal fade roo-combobox-touch-view',
19912 cls: 'modal-dialog',
19913 style : 'position:fixed', // we have to fix position....
19917 cls: 'modal-content',
19919 Roo.bootstrap.form.ComboBox.header,
19920 Roo.bootstrap.form.ComboBox.body,
19921 Roo.bootstrap.form.ComboBox.footer
19930 * Ext JS Library 1.1.1
19931 * Copyright(c) 2006-2007, Ext JS, LLC.
19933 * Originally Released Under LGPL - original licence link has changed is not relivant.
19936 * <script type="text/javascript">
19941 * @extends Roo.util.Observable
19942 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19943 * This class also supports single and multi selection modes. <br>
19944 * Create a data model bound view:
19946 var store = new Roo.data.Store(...);
19948 var view = new Roo.View({
19950 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19952 singleSelect: true,
19953 selectedClass: "ydataview-selected",
19957 // listen for node click?
19958 view.on("click", function(vw, index, node, e){
19959 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19963 dataModel.load("foobar.xml");
19965 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19967 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19968 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19970 * Note: old style constructor is still suported (container, template, config)
19973 * Create a new View
19974 * @param {Object} config The config object
19977 Roo.View = function(config, depreciated_tpl, depreciated_config){
19979 this.parent = false;
19981 if (typeof(depreciated_tpl) == 'undefined') {
19982 // new way.. - universal constructor.
19983 Roo.apply(this, config);
19984 this.el = Roo.get(this.el);
19987 this.el = Roo.get(config);
19988 this.tpl = depreciated_tpl;
19989 Roo.apply(this, depreciated_config);
19991 this.wrapEl = this.el.wrap().wrap();
19992 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19995 if(typeof(this.tpl) == "string"){
19996 this.tpl = new Roo.Template(this.tpl);
19998 // support xtype ctors..
19999 this.tpl = new Roo.factory(this.tpl, Roo);
20003 this.tpl.compile();
20008 * @event beforeclick
20009 * Fires before a click is processed. Returns false to cancel the default action.
20010 * @param {Roo.View} this
20011 * @param {Number} index The index of the target node
20012 * @param {HTMLElement} node The target node
20013 * @param {Roo.EventObject} e The raw event object
20015 "beforeclick" : true,
20018 * Fires when a template node is clicked.
20019 * @param {Roo.View} this
20020 * @param {Number} index The index of the target node
20021 * @param {HTMLElement} node The target node
20022 * @param {Roo.EventObject} e The raw event object
20027 * Fires when a template node is double clicked.
20028 * @param {Roo.View} this
20029 * @param {Number} index The index of the target node
20030 * @param {HTMLElement} node The target node
20031 * @param {Roo.EventObject} e The raw event object
20035 * @event contextmenu
20036 * Fires when a template node is right clicked.
20037 * @param {Roo.View} this
20038 * @param {Number} index The index of the target node
20039 * @param {HTMLElement} node The target node
20040 * @param {Roo.EventObject} e The raw event object
20042 "contextmenu" : true,
20044 * @event selectionchange
20045 * Fires when the selected nodes change.
20046 * @param {Roo.View} this
20047 * @param {Array} selections Array of the selected nodes
20049 "selectionchange" : true,
20052 * @event beforeselect
20053 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20054 * @param {Roo.View} this
20055 * @param {HTMLElement} node The node to be selected
20056 * @param {Array} selections Array of currently selected nodes
20058 "beforeselect" : true,
20060 * @event preparedata
20061 * Fires on every row to render, to allow you to change the data.
20062 * @param {Roo.View} this
20063 * @param {Object} data to be rendered (change this)
20065 "preparedata" : true
20073 "click": this.onClick,
20074 "dblclick": this.onDblClick,
20075 "contextmenu": this.onContextMenu,
20079 this.selections = [];
20081 this.cmp = new Roo.CompositeElementLite([]);
20083 this.store = Roo.factory(this.store, Roo.data);
20084 this.setStore(this.store, true);
20087 if ( this.footer && this.footer.xtype) {
20089 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20091 this.footer.dataSource = this.store;
20092 this.footer.container = fctr;
20093 this.footer = Roo.factory(this.footer, Roo);
20094 fctr.insertFirst(this.el);
20096 // this is a bit insane - as the paging toolbar seems to detach the el..
20097 // dom.parentNode.parentNode.parentNode
20098 // they get detached?
20102 Roo.View.superclass.constructor.call(this);
20107 Roo.extend(Roo.View, Roo.util.Observable, {
20110 * @cfg {Roo.data.Store} store Data store to load data from.
20115 * @cfg {String|Roo.Element} el The container element.
20120 * @cfg {String|Roo.Template} tpl The template used by this View
20124 * @cfg {String} dataName the named area of the template to use as the data area
20125 * Works with domtemplates roo-name="name"
20129 * @cfg {String} selectedClass The css class to add to selected nodes
20131 selectedClass : "x-view-selected",
20133 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20138 * @cfg {String} text to display on mask (default Loading)
20142 * @cfg {Boolean} multiSelect Allow multiple selection
20144 multiSelect : false,
20146 * @cfg {Boolean} singleSelect Allow single selection
20148 singleSelect: false,
20151 * @cfg {Boolean} toggleSelect - selecting
20153 toggleSelect : false,
20156 * @cfg {Boolean} tickable - selecting
20161 * Returns the element this view is bound to.
20162 * @return {Roo.Element}
20164 getEl : function(){
20165 return this.wrapEl;
20171 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20173 refresh : function(){
20174 //Roo.log('refresh');
20177 // if we are using something like 'domtemplate', then
20178 // the what gets used is:
20179 // t.applySubtemplate(NAME, data, wrapping data..)
20180 // the outer template then get' applied with
20181 // the store 'extra data'
20182 // and the body get's added to the
20183 // roo-name="data" node?
20184 // <span class='roo-tpl-{name}'></span> ?????
20188 this.clearSelections();
20189 this.el.update("");
20191 var records = this.store.getRange();
20192 if(records.length < 1) {
20194 // is this valid?? = should it render a template??
20196 this.el.update(this.emptyText);
20200 if (this.dataName) {
20201 this.el.update(t.apply(this.store.meta)); //????
20202 el = this.el.child('.roo-tpl-' + this.dataName);
20205 for(var i = 0, len = records.length; i < len; i++){
20206 var data = this.prepareData(records[i].data, i, records[i]);
20207 this.fireEvent("preparedata", this, data, i, records[i]);
20209 var d = Roo.apply({}, data);
20212 Roo.apply(d, {'roo-id' : Roo.id()});
20216 Roo.each(this.parent.item, function(item){
20217 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20220 Roo.apply(d, {'roo-data-checked' : 'checked'});
20224 html[html.length] = Roo.util.Format.trim(
20226 t.applySubtemplate(this.dataName, d, this.store.meta) :
20233 el.update(html.join(""));
20234 this.nodes = el.dom.childNodes;
20235 this.updateIndexes(0);
20240 * Function to override to reformat the data that is sent to
20241 * the template for each node.
20242 * DEPRICATED - use the preparedata event handler.
20243 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20244 * a JSON object for an UpdateManager bound view).
20246 prepareData : function(data, index, record)
20248 this.fireEvent("preparedata", this, data, index, record);
20252 onUpdate : function(ds, record){
20253 // Roo.log('on update');
20254 this.clearSelections();
20255 var index = this.store.indexOf(record);
20256 var n = this.nodes[index];
20257 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20258 n.parentNode.removeChild(n);
20259 this.updateIndexes(index, index);
20265 onAdd : function(ds, records, index)
20267 //Roo.log(['on Add', ds, records, index] );
20268 this.clearSelections();
20269 if(this.nodes.length == 0){
20273 var n = this.nodes[index];
20274 for(var i = 0, len = records.length; i < len; i++){
20275 var d = this.prepareData(records[i].data, i, records[i]);
20277 this.tpl.insertBefore(n, d);
20280 this.tpl.append(this.el, d);
20283 this.updateIndexes(index);
20286 onRemove : function(ds, record, index){
20287 // Roo.log('onRemove');
20288 this.clearSelections();
20289 var el = this.dataName ?
20290 this.el.child('.roo-tpl-' + this.dataName) :
20293 el.dom.removeChild(this.nodes[index]);
20294 this.updateIndexes(index);
20298 * Refresh an individual node.
20299 * @param {Number} index
20301 refreshNode : function(index){
20302 this.onUpdate(this.store, this.store.getAt(index));
20305 updateIndexes : function(startIndex, endIndex){
20306 var ns = this.nodes;
20307 startIndex = startIndex || 0;
20308 endIndex = endIndex || ns.length - 1;
20309 for(var i = startIndex; i <= endIndex; i++){
20310 ns[i].nodeIndex = i;
20315 * Changes the data store this view uses and refresh the view.
20316 * @param {Store} store
20318 setStore : function(store, initial){
20319 if(!initial && this.store){
20320 this.store.un("datachanged", this.refresh);
20321 this.store.un("add", this.onAdd);
20322 this.store.un("remove", this.onRemove);
20323 this.store.un("update", this.onUpdate);
20324 this.store.un("clear", this.refresh);
20325 this.store.un("beforeload", this.onBeforeLoad);
20326 this.store.un("load", this.onLoad);
20327 this.store.un("loadexception", this.onLoad);
20331 store.on("datachanged", this.refresh, this);
20332 store.on("add", this.onAdd, this);
20333 store.on("remove", this.onRemove, this);
20334 store.on("update", this.onUpdate, this);
20335 store.on("clear", this.refresh, this);
20336 store.on("beforeload", this.onBeforeLoad, this);
20337 store.on("load", this.onLoad, this);
20338 store.on("loadexception", this.onLoad, this);
20346 * onbeforeLoad - masks the loading area.
20349 onBeforeLoad : function(store,opts)
20351 //Roo.log('onBeforeLoad');
20353 this.el.update("");
20355 this.el.mask(this.mask ? this.mask : "Loading" );
20357 onLoad : function ()
20364 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20365 * @param {HTMLElement} node
20366 * @return {HTMLElement} The template node
20368 findItemFromChild : function(node){
20369 var el = this.dataName ?
20370 this.el.child('.roo-tpl-' + this.dataName,true) :
20373 if(!node || node.parentNode == el){
20376 var p = node.parentNode;
20377 while(p && p != el){
20378 if(p.parentNode == el){
20387 onClick : function(e){
20388 var item = this.findItemFromChild(e.getTarget());
20390 var index = this.indexOf(item);
20391 if(this.onItemClick(item, index, e) !== false){
20392 this.fireEvent("click", this, index, item, e);
20395 this.clearSelections();
20400 onContextMenu : function(e){
20401 var item = this.findItemFromChild(e.getTarget());
20403 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20408 onDblClick : function(e){
20409 var item = this.findItemFromChild(e.getTarget());
20411 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20415 onItemClick : function(item, index, e)
20417 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20420 if (this.toggleSelect) {
20421 var m = this.isSelected(item) ? 'unselect' : 'select';
20424 _t[m](item, true, false);
20427 if(this.multiSelect || this.singleSelect){
20428 if(this.multiSelect && e.shiftKey && this.lastSelection){
20429 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20431 this.select(item, this.multiSelect && e.ctrlKey);
20432 this.lastSelection = item;
20435 if(!this.tickable){
20436 e.preventDefault();
20444 * Get the number of selected nodes.
20447 getSelectionCount : function(){
20448 return this.selections.length;
20452 * Get the currently selected nodes.
20453 * @return {Array} An array of HTMLElements
20455 getSelectedNodes : function(){
20456 return this.selections;
20460 * Get the indexes of the selected nodes.
20463 getSelectedIndexes : function(){
20464 var indexes = [], s = this.selections;
20465 for(var i = 0, len = s.length; i < len; i++){
20466 indexes.push(s[i].nodeIndex);
20472 * Clear all selections
20473 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20475 clearSelections : function(suppressEvent){
20476 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20477 this.cmp.elements = this.selections;
20478 this.cmp.removeClass(this.selectedClass);
20479 this.selections = [];
20480 if(!suppressEvent){
20481 this.fireEvent("selectionchange", this, this.selections);
20487 * Returns true if the passed node is selected
20488 * @param {HTMLElement/Number} node The node or node index
20489 * @return {Boolean}
20491 isSelected : function(node){
20492 var s = this.selections;
20496 node = this.getNode(node);
20497 return s.indexOf(node) !== -1;
20502 * @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
20503 * @param {Boolean} keepExisting (optional) true to keep existing selections
20504 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20506 select : function(nodeInfo, keepExisting, suppressEvent){
20507 if(nodeInfo instanceof Array){
20509 this.clearSelections(true);
20511 for(var i = 0, len = nodeInfo.length; i < len; i++){
20512 this.select(nodeInfo[i], true, true);
20516 var node = this.getNode(nodeInfo);
20517 if(!node || this.isSelected(node)){
20518 return; // already selected.
20521 this.clearSelections(true);
20524 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20525 Roo.fly(node).addClass(this.selectedClass);
20526 this.selections.push(node);
20527 if(!suppressEvent){
20528 this.fireEvent("selectionchange", this, this.selections);
20536 * @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
20537 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20538 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20540 unselect : function(nodeInfo, keepExisting, suppressEvent)
20542 if(nodeInfo instanceof Array){
20543 Roo.each(this.selections, function(s) {
20544 this.unselect(s, nodeInfo);
20548 var node = this.getNode(nodeInfo);
20549 if(!node || !this.isSelected(node)){
20550 //Roo.log("not selected");
20551 return; // not selected.
20555 Roo.each(this.selections, function(s) {
20557 Roo.fly(node).removeClass(this.selectedClass);
20564 this.selections= ns;
20565 this.fireEvent("selectionchange", this, this.selections);
20569 * Gets a template node.
20570 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20571 * @return {HTMLElement} The node or null if it wasn't found
20573 getNode : function(nodeInfo){
20574 if(typeof nodeInfo == "string"){
20575 return document.getElementById(nodeInfo);
20576 }else if(typeof nodeInfo == "number"){
20577 return this.nodes[nodeInfo];
20583 * Gets a range template nodes.
20584 * @param {Number} startIndex
20585 * @param {Number} endIndex
20586 * @return {Array} An array of nodes
20588 getNodes : function(start, end){
20589 var ns = this.nodes;
20590 start = start || 0;
20591 end = typeof end == "undefined" ? ns.length - 1 : end;
20594 for(var i = start; i <= end; i++){
20598 for(var i = start; i >= end; i--){
20606 * Finds the index of the passed node
20607 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20608 * @return {Number} The index of the node or -1
20610 indexOf : function(node){
20611 node = this.getNode(node);
20612 if(typeof node.nodeIndex == "number"){
20613 return node.nodeIndex;
20615 var ns = this.nodes;
20616 for(var i = 0, len = ns.length; i < len; i++){
20627 * based on jquery fullcalendar
20631 Roo.bootstrap = Roo.bootstrap || {};
20633 * @class Roo.bootstrap.Calendar
20634 * @extends Roo.bootstrap.Component
20635 * Bootstrap Calendar class
20636 * @cfg {Boolean} loadMask (true|false) default false
20637 * @cfg {Object} header generate the user specific header of the calendar, default false
20640 * Create a new Container
20641 * @param {Object} config The config object
20646 Roo.bootstrap.Calendar = function(config){
20647 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20651 * Fires when a date is selected
20652 * @param {DatePicker} this
20653 * @param {Date} date The selected date
20657 * @event monthchange
20658 * Fires when the displayed month changes
20659 * @param {DatePicker} this
20660 * @param {Date} date The selected month
20662 'monthchange': true,
20664 * @event evententer
20665 * Fires when mouse over an event
20666 * @param {Calendar} this
20667 * @param {event} Event
20669 'evententer': true,
20671 * @event eventleave
20672 * Fires when the mouse leaves an
20673 * @param {Calendar} this
20676 'eventleave': true,
20678 * @event eventclick
20679 * Fires when the mouse click an
20680 * @param {Calendar} this
20689 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20692 * @cfg {Roo.data.Store} store
20693 * The data source for the calendar
20697 * @cfg {Number} startDay
20698 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20706 getAutoCreate : function(){
20709 var fc_button = function(name, corner, style, content ) {
20710 return Roo.apply({},{
20712 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20714 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20717 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20728 style : 'width:100%',
20735 cls : 'fc-header-left',
20737 fc_button('prev', 'left', 'arrow', '‹' ),
20738 fc_button('next', 'right', 'arrow', '›' ),
20739 { tag: 'span', cls: 'fc-header-space' },
20740 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20748 cls : 'fc-header-center',
20752 cls: 'fc-header-title',
20755 html : 'month / year'
20763 cls : 'fc-header-right',
20765 /* fc_button('month', 'left', '', 'month' ),
20766 fc_button('week', '', '', 'week' ),
20767 fc_button('day', 'right', '', 'day' )
20779 header = this.header;
20782 var cal_heads = function() {
20784 // fixme - handle this.
20786 for (var i =0; i < Date.dayNames.length; i++) {
20787 var d = Date.dayNames[i];
20790 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20791 html : d.substring(0,3)
20795 ret[0].cls += ' fc-first';
20796 ret[6].cls += ' fc-last';
20799 var cal_cell = function(n) {
20802 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20807 cls: 'fc-day-number',
20811 cls: 'fc-day-content',
20815 style: 'position: relative;' // height: 17px;
20827 var cal_rows = function() {
20830 for (var r = 0; r < 6; r++) {
20837 for (var i =0; i < Date.dayNames.length; i++) {
20838 var d = Date.dayNames[i];
20839 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20842 row.cn[0].cls+=' fc-first';
20843 row.cn[0].cn[0].style = 'min-height:90px';
20844 row.cn[6].cls+=' fc-last';
20848 ret[0].cls += ' fc-first';
20849 ret[4].cls += ' fc-prev-last';
20850 ret[5].cls += ' fc-last';
20857 cls: 'fc-border-separate',
20858 style : 'width:100%',
20866 cls : 'fc-first fc-last',
20884 cls : 'fc-content',
20885 style : "position: relative;",
20888 cls : 'fc-view fc-view-month fc-grid',
20889 style : 'position: relative',
20890 unselectable : 'on',
20893 cls : 'fc-event-container',
20894 style : 'position:absolute;z-index:8;top:0;left:0;'
20912 initEvents : function()
20915 throw "can not find store for calendar";
20921 style: "text-align:center",
20925 style: "background-color:white;width:50%;margin:250 auto",
20929 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20940 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20942 var size = this.el.select('.fc-content', true).first().getSize();
20943 this.maskEl.setSize(size.width, size.height);
20944 this.maskEl.enableDisplayMode("block");
20945 if(!this.loadMask){
20946 this.maskEl.hide();
20949 this.store = Roo.factory(this.store, Roo.data);
20950 this.store.on('load', this.onLoad, this);
20951 this.store.on('beforeload', this.onBeforeLoad, this);
20955 this.cells = this.el.select('.fc-day',true);
20956 //Roo.log(this.cells);
20957 this.textNodes = this.el.query('.fc-day-number');
20958 this.cells.addClassOnOver('fc-state-hover');
20960 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20961 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20962 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20963 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20965 this.on('monthchange', this.onMonthChange, this);
20967 this.update(new Date().clearTime());
20970 resize : function() {
20971 var sz = this.el.getSize();
20973 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20974 this.el.select('.fc-day-content div',true).setHeight(34);
20979 showPrevMonth : function(e){
20980 this.update(this.activeDate.add("mo", -1));
20982 showToday : function(e){
20983 this.update(new Date().clearTime());
20986 showNextMonth : function(e){
20987 this.update(this.activeDate.add("mo", 1));
20991 showPrevYear : function(){
20992 this.update(this.activeDate.add("y", -1));
20996 showNextYear : function(){
20997 this.update(this.activeDate.add("y", 1));
21002 update : function(date)
21004 var vd = this.activeDate;
21005 this.activeDate = date;
21006 // if(vd && this.el){
21007 // var t = date.getTime();
21008 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21009 // Roo.log('using add remove');
21011 // this.fireEvent('monthchange', this, date);
21013 // this.cells.removeClass("fc-state-highlight");
21014 // this.cells.each(function(c){
21015 // if(c.dateValue == t){
21016 // c.addClass("fc-state-highlight");
21017 // setTimeout(function(){
21018 // try{c.dom.firstChild.focus();}catch(e){}
21028 var days = date.getDaysInMonth();
21030 var firstOfMonth = date.getFirstDateOfMonth();
21031 var startingPos = firstOfMonth.getDay()-this.startDay;
21033 if(startingPos < this.startDay){
21037 var pm = date.add(Date.MONTH, -1);
21038 var prevStart = pm.getDaysInMonth()-startingPos;
21040 this.cells = this.el.select('.fc-day',true);
21041 this.textNodes = this.el.query('.fc-day-number');
21042 this.cells.addClassOnOver('fc-state-hover');
21044 var cells = this.cells.elements;
21045 var textEls = this.textNodes;
21047 Roo.each(cells, function(cell){
21048 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21051 days += startingPos;
21053 // convert everything to numbers so it's fast
21054 var day = 86400000;
21055 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21058 //Roo.log(prevStart);
21060 var today = new Date().clearTime().getTime();
21061 var sel = date.clearTime().getTime();
21062 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21063 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21064 var ddMatch = this.disabledDatesRE;
21065 var ddText = this.disabledDatesText;
21066 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21067 var ddaysText = this.disabledDaysText;
21068 var format = this.format;
21070 var setCellClass = function(cal, cell){
21074 //Roo.log('set Cell Class');
21076 var t = d.getTime();
21080 cell.dateValue = t;
21082 cell.className += " fc-today";
21083 cell.className += " fc-state-highlight";
21084 cell.title = cal.todayText;
21087 // disable highlight in other month..
21088 //cell.className += " fc-state-highlight";
21093 cell.className = " fc-state-disabled";
21094 cell.title = cal.minText;
21098 cell.className = " fc-state-disabled";
21099 cell.title = cal.maxText;
21103 if(ddays.indexOf(d.getDay()) != -1){
21104 cell.title = ddaysText;
21105 cell.className = " fc-state-disabled";
21108 if(ddMatch && format){
21109 var fvalue = d.dateFormat(format);
21110 if(ddMatch.test(fvalue)){
21111 cell.title = ddText.replace("%0", fvalue);
21112 cell.className = " fc-state-disabled";
21116 if (!cell.initialClassName) {
21117 cell.initialClassName = cell.dom.className;
21120 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21125 for(; i < startingPos; i++) {
21126 textEls[i].innerHTML = (++prevStart);
21127 d.setDate(d.getDate()+1);
21129 cells[i].className = "fc-past fc-other-month";
21130 setCellClass(this, cells[i]);
21135 for(; i < days; i++){
21136 intDay = i - startingPos + 1;
21137 textEls[i].innerHTML = (intDay);
21138 d.setDate(d.getDate()+1);
21140 cells[i].className = ''; // "x-date-active";
21141 setCellClass(this, cells[i]);
21145 for(; i < 42; i++) {
21146 textEls[i].innerHTML = (++extraDays);
21147 d.setDate(d.getDate()+1);
21149 cells[i].className = "fc-future fc-other-month";
21150 setCellClass(this, cells[i]);
21153 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21155 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21157 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21158 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21160 if(totalRows != 6){
21161 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21162 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21165 this.fireEvent('monthchange', this, date);
21169 if(!this.internalRender){
21170 var main = this.el.dom.firstChild;
21171 var w = main.offsetWidth;
21172 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21173 Roo.fly(main).setWidth(w);
21174 this.internalRender = true;
21175 // opera does not respect the auto grow header center column
21176 // then, after it gets a width opera refuses to recalculate
21177 // without a second pass
21178 if(Roo.isOpera && !this.secondPass){
21179 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21180 this.secondPass = true;
21181 this.update.defer(10, this, [date]);
21188 findCell : function(dt) {
21189 dt = dt.clearTime().getTime();
21191 this.cells.each(function(c){
21192 //Roo.log("check " +c.dateValue + '?=' + dt);
21193 if(c.dateValue == dt){
21203 findCells : function(ev) {
21204 var s = ev.start.clone().clearTime().getTime();
21206 var e= ev.end.clone().clearTime().getTime();
21209 this.cells.each(function(c){
21210 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21212 if(c.dateValue > e){
21215 if(c.dateValue < s){
21224 // findBestRow: function(cells)
21228 // for (var i =0 ; i < cells.length;i++) {
21229 // ret = Math.max(cells[i].rows || 0,ret);
21236 addItem : function(ev)
21238 // look for vertical location slot in
21239 var cells = this.findCells(ev);
21241 // ev.row = this.findBestRow(cells);
21243 // work out the location.
21247 for(var i =0; i < cells.length; i++) {
21249 cells[i].row = cells[0].row;
21252 cells[i].row = cells[i].row + 1;
21262 if (crow.start.getY() == cells[i].getY()) {
21264 crow.end = cells[i];
21281 cells[0].events.push(ev);
21283 this.calevents.push(ev);
21286 clearEvents: function() {
21288 if(!this.calevents){
21292 Roo.each(this.cells.elements, function(c){
21298 Roo.each(this.calevents, function(e) {
21299 Roo.each(e.els, function(el) {
21300 el.un('mouseenter' ,this.onEventEnter, this);
21301 el.un('mouseleave' ,this.onEventLeave, this);
21306 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21312 renderEvents: function()
21316 this.cells.each(function(c) {
21325 if(c.row != c.events.length){
21326 r = 4 - (4 - (c.row - c.events.length));
21329 c.events = ev.slice(0, r);
21330 c.more = ev.slice(r);
21332 if(c.more.length && c.more.length == 1){
21333 c.events.push(c.more.pop());
21336 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21340 this.cells.each(function(c) {
21342 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21345 for (var e = 0; e < c.events.length; e++){
21346 var ev = c.events[e];
21347 var rows = ev.rows;
21349 for(var i = 0; i < rows.length; i++) {
21351 // how many rows should it span..
21354 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21355 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21357 unselectable : "on",
21360 cls: 'fc-event-inner',
21364 // cls: 'fc-event-time',
21365 // html : cells.length > 1 ? '' : ev.time
21369 cls: 'fc-event-title',
21370 html : String.format('{0}', ev.title)
21377 cls: 'ui-resizable-handle ui-resizable-e',
21378 html : '  '
21385 cfg.cls += ' fc-event-start';
21387 if ((i+1) == rows.length) {
21388 cfg.cls += ' fc-event-end';
21391 var ctr = _this.el.select('.fc-event-container',true).first();
21392 var cg = ctr.createChild(cfg);
21394 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21395 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21397 var r = (c.more.length) ? 1 : 0;
21398 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21399 cg.setWidth(ebox.right - sbox.x -2);
21401 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21402 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21403 cg.on('click', _this.onEventClick, _this, ev);
21414 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21415 style : 'position: absolute',
21416 unselectable : "on",
21419 cls: 'fc-event-inner',
21423 cls: 'fc-event-title',
21431 cls: 'ui-resizable-handle ui-resizable-e',
21432 html : '  '
21438 var ctr = _this.el.select('.fc-event-container',true).first();
21439 var cg = ctr.createChild(cfg);
21441 var sbox = c.select('.fc-day-content',true).first().getBox();
21442 var ebox = c.select('.fc-day-content',true).first().getBox();
21444 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21445 cg.setWidth(ebox.right - sbox.x -2);
21447 cg.on('click', _this.onMoreEventClick, _this, c.more);
21457 onEventEnter: function (e, el,event,d) {
21458 this.fireEvent('evententer', this, el, event);
21461 onEventLeave: function (e, el,event,d) {
21462 this.fireEvent('eventleave', this, el, event);
21465 onEventClick: function (e, el,event,d) {
21466 this.fireEvent('eventclick', this, el, event);
21469 onMonthChange: function () {
21473 onMoreEventClick: function(e, el, more)
21477 this.calpopover.placement = 'right';
21478 this.calpopover.setTitle('More');
21480 this.calpopover.setContent('');
21482 var ctr = this.calpopover.el.select('.popover-content', true).first();
21484 Roo.each(more, function(m){
21486 cls : 'fc-event-hori fc-event-draggable',
21489 var cg = ctr.createChild(cfg);
21491 cg.on('click', _this.onEventClick, _this, m);
21494 this.calpopover.show(el);
21499 onLoad: function ()
21501 this.calevents = [];
21504 if(this.store.getCount() > 0){
21505 this.store.data.each(function(d){
21508 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21509 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21510 time : d.data.start_time,
21511 title : d.data.title,
21512 description : d.data.description,
21513 venue : d.data.venue
21518 this.renderEvents();
21520 if(this.calevents.length && this.loadMask){
21521 this.maskEl.hide();
21525 onBeforeLoad: function()
21527 this.clearEvents();
21529 this.maskEl.show();
21543 * @class Roo.bootstrap.Popover
21544 * @extends Roo.bootstrap.Component
21545 * @parent none builder
21546 * @children Roo.bootstrap.Component
21547 * Bootstrap Popover class
21548 * @cfg {String} html contents of the popover (or false to use children..)
21549 * @cfg {String} title of popover (or false to hide)
21550 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21551 * @cfg {String} trigger click || hover (or false to trigger manually)
21552 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21553 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21554 * - if false and it has a 'parent' then it will be automatically added to that element
21555 * - if string - Roo.get will be called
21556 * @cfg {Number} delay - delay before showing
21559 * Create a new Popover
21560 * @param {Object} config The config object
21563 Roo.bootstrap.Popover = function(config){
21564 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21570 * After the popover show
21572 * @param {Roo.bootstrap.Popover} this
21577 * After the popover hide
21579 * @param {Roo.bootstrap.Popover} this
21585 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21590 placement : 'right',
21591 trigger : 'hover', // hover
21597 can_build_overlaid : false,
21599 maskEl : false, // the mask element
21602 alignEl : false, // when show is called with an element - this get's stored.
21604 getChildContainer : function()
21606 return this.contentEl;
21609 getPopoverHeader : function()
21611 this.title = true; // flag not to hide it..
21612 this.headerEl.addClass('p-0');
21613 return this.headerEl
21617 getAutoCreate : function(){
21620 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21621 style: 'display:block',
21627 cls : 'popover-inner ',
21631 cls: 'popover-title popover-header',
21632 html : this.title === false ? '' : this.title
21635 cls : 'popover-content popover-body ' + (this.cls || ''),
21636 html : this.html || ''
21647 * @param {string} the title
21649 setTitle: function(str)
21653 this.headerEl.dom.innerHTML = str;
21658 * @param {string} the body content
21660 setContent: function(str)
21663 if (this.contentEl) {
21664 this.contentEl.dom.innerHTML = str;
21668 // as it get's added to the bottom of the page.
21669 onRender : function(ct, position)
21671 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21676 var cfg = Roo.apply({}, this.getAutoCreate());
21680 cfg.cls += ' ' + this.cls;
21683 cfg.style = this.style;
21685 //Roo.log("adding to ");
21686 this.el = Roo.get(document.body).createChild(cfg, position);
21687 // Roo.log(this.el);
21690 this.contentEl = this.el.select('.popover-content',true).first();
21691 this.headerEl = this.el.select('.popover-title',true).first();
21694 if(typeof(this.items) != 'undefined'){
21695 var items = this.items;
21698 for(var i =0;i < items.length;i++) {
21699 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21703 this.items = nitems;
21705 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21706 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21713 resizeMask : function()
21715 this.maskEl.setSize(
21716 Roo.lib.Dom.getViewWidth(true),
21717 Roo.lib.Dom.getViewHeight(true)
21721 initEvents : function()
21725 Roo.bootstrap.Popover.register(this);
21728 this.arrowEl = this.el.select('.arrow',true).first();
21729 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21730 this.el.enableDisplayMode('block');
21734 if (this.over === false && !this.parent()) {
21737 if (this.triggers === false) {
21742 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21743 var triggers = this.trigger ? this.trigger.split(' ') : [];
21744 Roo.each(triggers, function(trigger) {
21746 if (trigger == 'click') {
21747 on_el.on('click', this.toggle, this);
21748 } else if (trigger != 'manual') {
21749 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21750 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21752 on_el.on(eventIn ,this.enter, this);
21753 on_el.on(eventOut, this.leave, this);
21763 toggle : function () {
21764 this.hoverState == 'in' ? this.leave() : this.enter();
21767 enter : function () {
21769 clearTimeout(this.timeout);
21771 this.hoverState = 'in';
21773 if (!this.delay || !this.delay.show) {
21778 this.timeout = setTimeout(function () {
21779 if (_t.hoverState == 'in') {
21782 }, this.delay.show)
21785 leave : function() {
21786 clearTimeout(this.timeout);
21788 this.hoverState = 'out';
21790 if (!this.delay || !this.delay.hide) {
21795 this.timeout = setTimeout(function () {
21796 if (_t.hoverState == 'out') {
21799 }, this.delay.hide)
21803 * update the position of the dialog
21804 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21809 doAlign : function()
21812 if (this.alignEl) {
21813 this.updatePosition(this.placement, true);
21816 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21817 var es = this.el.getSize();
21818 var x = Roo.lib.Dom.getViewWidth()/2;
21819 var y = Roo.lib.Dom.getViewHeight()/2;
21820 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21832 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21833 * @param {string} (left|right|top|bottom) position
21835 show : function (on_el, placement)
21837 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21838 on_el = on_el || false; // default to false
21841 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21842 on_el = this.parent().el;
21843 } else if (this.over) {
21844 on_el = Roo.get(this.over);
21849 this.alignEl = Roo.get( on_el );
21852 this.render(document.body);
21858 if (this.title === false) {
21859 this.headerEl.hide();
21864 this.el.dom.style.display = 'block';
21868 //var arrow = this.el.select('.arrow',true).first();
21869 //arrow.set(align[2],
21871 this.el.addClass('in');
21875 this.hoverState = 'in';
21878 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21879 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21880 this.maskEl.dom.style.display = 'block';
21881 this.maskEl.addClass('show');
21883 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21885 this.fireEvent('show', this);
21889 * fire this manually after loading a grid in the table for example
21890 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21891 * @param {Boolean} try and move it if we cant get right position.
21893 updatePosition : function(placement, try_move)
21895 // allow for calling with no parameters
21896 placement = placement ? placement : this.placement;
21897 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21899 this.el.removeClass([
21900 'fade','top','bottom', 'left', 'right','in',
21901 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21903 this.el.addClass(placement + ' bs-popover-' + placement);
21905 if (!this.alignEl ) {
21909 switch (placement) {
21911 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21912 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21913 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21914 //normal display... or moved up/down.
21915 this.el.setXY(offset);
21916 var xy = this.alignEl.getAnchorXY('tr', false);
21918 this.arrowEl.setXY(xy);
21921 // continue through...
21922 return this.updatePosition('left', false);
21926 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21927 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21928 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21929 //normal display... or moved up/down.
21930 this.el.setXY(offset);
21931 var xy = this.alignEl.getAnchorXY('tl', false);
21932 xy[0]-=10;xy[1]+=5; // << fix me
21933 this.arrowEl.setXY(xy);
21937 return this.updatePosition('right', false);
21940 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21941 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21942 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21943 //normal display... or moved up/down.
21944 this.el.setXY(offset);
21945 var xy = this.alignEl.getAnchorXY('t', false);
21946 xy[1]-=10; // << fix me
21947 this.arrowEl.setXY(xy);
21951 return this.updatePosition('bottom', false);
21954 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21955 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21956 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21957 //normal display... or moved up/down.
21958 this.el.setXY(offset);
21959 var xy = this.alignEl.getAnchorXY('b', false);
21960 xy[1]+=2; // << fix me
21961 this.arrowEl.setXY(xy);
21965 return this.updatePosition('top', false);
21976 this.el.setXY([0,0]);
21977 this.el.removeClass('in');
21979 this.hoverState = null;
21980 this.maskEl.hide(); // always..
21981 this.fireEvent('hide', this);
21987 Roo.apply(Roo.bootstrap.Popover, {
21990 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21991 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21992 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21993 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21998 clickHander : false,
22002 onMouseDown : function(e)
22004 if (this.popups.length && !e.getTarget(".roo-popover")) {
22005 /// what is nothing is showing..
22014 register : function(popup)
22016 if (!Roo.bootstrap.Popover.clickHandler) {
22017 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22019 // hide other popups.
22020 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22021 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22022 this.hideAll(); //<< why?
22023 //this.popups.push(popup);
22025 hideAll : function()
22027 this.popups.forEach(function(p) {
22031 onShow : function() {
22032 Roo.bootstrap.Popover.popups.push(this);
22034 onHide : function() {
22035 Roo.bootstrap.Popover.popups.remove(this);
22040 * @class Roo.bootstrap.PopoverNav
22041 * @extends Roo.bootstrap.nav.Simplebar
22042 * @parent Roo.bootstrap.Popover
22043 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22045 * Bootstrap Popover header navigation class
22046 * FIXME? should this go under nav?
22050 * Create a new Popover Header Navigation
22051 * @param {Object} config The config object
22054 Roo.bootstrap.PopoverNav = function(config){
22055 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22058 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22061 container_method : 'getPopoverHeader'
22079 * @class Roo.bootstrap.Progress
22080 * @extends Roo.bootstrap.Component
22081 * @children Roo.bootstrap.ProgressBar
22082 * Bootstrap Progress class
22083 * @cfg {Boolean} striped striped of the progress bar
22084 * @cfg {Boolean} active animated of the progress bar
22088 * Create a new Progress
22089 * @param {Object} config The config object
22092 Roo.bootstrap.Progress = function(config){
22093 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22096 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22101 getAutoCreate : function(){
22109 cfg.cls += ' progress-striped';
22113 cfg.cls += ' active';
22132 * @class Roo.bootstrap.ProgressBar
22133 * @extends Roo.bootstrap.Component
22134 * Bootstrap ProgressBar class
22135 * @cfg {Number} aria_valuenow aria-value now
22136 * @cfg {Number} aria_valuemin aria-value min
22137 * @cfg {Number} aria_valuemax aria-value max
22138 * @cfg {String} label label for the progress bar
22139 * @cfg {String} panel (success | info | warning | danger )
22140 * @cfg {String} role role of the progress bar
22141 * @cfg {String} sr_only text
22145 * Create a new ProgressBar
22146 * @param {Object} config The config object
22149 Roo.bootstrap.ProgressBar = function(config){
22150 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22153 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22157 aria_valuemax : 100,
22163 getAutoCreate : function()
22168 cls: 'progress-bar',
22169 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22181 cfg.role = this.role;
22184 if(this.aria_valuenow){
22185 cfg['aria-valuenow'] = this.aria_valuenow;
22188 if(this.aria_valuemin){
22189 cfg['aria-valuemin'] = this.aria_valuemin;
22192 if(this.aria_valuemax){
22193 cfg['aria-valuemax'] = this.aria_valuemax;
22196 if(this.label && !this.sr_only){
22197 cfg.html = this.label;
22201 cfg.cls += ' progress-bar-' + this.panel;
22207 update : function(aria_valuenow)
22209 this.aria_valuenow = aria_valuenow;
22211 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22219 * @class Roo.bootstrap.TabGroup
22220 * @extends Roo.bootstrap.Column
22221 * @children Roo.bootstrap.TabPanel
22222 * Bootstrap Column class
22223 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22224 * @cfg {Boolean} carousel true to make the group behave like a carousel
22225 * @cfg {Boolean} bullets show bullets for the panels
22226 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22227 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22228 * @cfg {Boolean} showarrow (true|false) show arrow default true
22231 * Create a new TabGroup
22232 * @param {Object} config The config object
22235 Roo.bootstrap.TabGroup = function(config){
22236 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22238 this.navId = Roo.id();
22241 Roo.bootstrap.TabGroup.register(this);
22245 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22248 transition : false,
22253 slideOnTouch : false,
22256 getAutoCreate : function()
22258 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22260 cfg.cls += ' tab-content';
22262 if (this.carousel) {
22263 cfg.cls += ' carousel slide';
22266 cls : 'carousel-inner',
22270 if(this.bullets && !Roo.isTouch){
22273 cls : 'carousel-bullets',
22277 if(this.bullets_cls){
22278 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22285 cfg.cn[0].cn.push(bullets);
22288 if(this.showarrow){
22289 cfg.cn[0].cn.push({
22291 class : 'carousel-arrow',
22295 class : 'carousel-prev',
22299 class : 'fa fa-chevron-left'
22305 class : 'carousel-next',
22309 class : 'fa fa-chevron-right'
22322 initEvents: function()
22324 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22325 // this.el.on("touchstart", this.onTouchStart, this);
22328 if(this.autoslide){
22331 this.slideFn = window.setInterval(function() {
22332 _this.showPanelNext();
22336 if(this.showarrow){
22337 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22338 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22344 // onTouchStart : function(e, el, o)
22346 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22350 // this.showPanelNext();
22354 getChildContainer : function()
22356 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22360 * register a Navigation item
22361 * @param {Roo.bootstrap.nav.Item} the navitem to add
22363 register : function(item)
22365 this.tabs.push( item);
22366 item.navId = this.navId; // not really needed..
22371 getActivePanel : function()
22374 Roo.each(this.tabs, function(t) {
22384 getPanelByName : function(n)
22387 Roo.each(this.tabs, function(t) {
22388 if (t.tabId == n) {
22396 indexOfPanel : function(p)
22399 Roo.each(this.tabs, function(t,i) {
22400 if (t.tabId == p.tabId) {
22409 * show a specific panel
22410 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22411 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22413 showPanel : function (pan)
22415 if(this.transition || typeof(pan) == 'undefined'){
22416 Roo.log("waiting for the transitionend");
22420 if (typeof(pan) == 'number') {
22421 pan = this.tabs[pan];
22424 if (typeof(pan) == 'string') {
22425 pan = this.getPanelByName(pan);
22428 var cur = this.getActivePanel();
22431 Roo.log('pan or acitve pan is undefined');
22435 if (pan.tabId == this.getActivePanel().tabId) {
22439 if (false === cur.fireEvent('beforedeactivate')) {
22443 if(this.bullets > 0 && !Roo.isTouch){
22444 this.setActiveBullet(this.indexOfPanel(pan));
22447 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22449 //class="carousel-item carousel-item-next carousel-item-left"
22451 this.transition = true;
22452 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22453 var lr = dir == 'next' ? 'left' : 'right';
22454 pan.el.addClass(dir); // or prev
22455 pan.el.addClass('carousel-item-' + dir); // or prev
22456 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22457 cur.el.addClass(lr); // or right
22458 pan.el.addClass(lr);
22459 cur.el.addClass('carousel-item-' +lr); // or right
22460 pan.el.addClass('carousel-item-' +lr);
22464 cur.el.on('transitionend', function() {
22465 Roo.log("trans end?");
22467 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22468 pan.setActive(true);
22470 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22471 cur.setActive(false);
22473 _this.transition = false;
22475 }, this, { single: true } );
22480 cur.setActive(false);
22481 pan.setActive(true);
22486 showPanelNext : function()
22488 var i = this.indexOfPanel(this.getActivePanel());
22490 if (i >= this.tabs.length - 1 && !this.autoslide) {
22494 if (i >= this.tabs.length - 1 && this.autoslide) {
22498 this.showPanel(this.tabs[i+1]);
22501 showPanelPrev : function()
22503 var i = this.indexOfPanel(this.getActivePanel());
22505 if (i < 1 && !this.autoslide) {
22509 if (i < 1 && this.autoslide) {
22510 i = this.tabs.length;
22513 this.showPanel(this.tabs[i-1]);
22517 addBullet: function()
22519 if(!this.bullets || Roo.isTouch){
22522 var ctr = this.el.select('.carousel-bullets',true).first();
22523 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22524 var bullet = ctr.createChild({
22525 cls : 'bullet bullet-' + i
22526 },ctr.dom.lastChild);
22531 bullet.on('click', (function(e, el, o, ii, t){
22533 e.preventDefault();
22535 this.showPanel(ii);
22537 if(this.autoslide && this.slideFn){
22538 clearInterval(this.slideFn);
22539 this.slideFn = window.setInterval(function() {
22540 _this.showPanelNext();
22544 }).createDelegate(this, [i, bullet], true));
22549 setActiveBullet : function(i)
22555 Roo.each(this.el.select('.bullet', true).elements, function(el){
22556 el.removeClass('selected');
22559 var bullet = this.el.select('.bullet-' + i, true).first();
22565 bullet.addClass('selected');
22576 Roo.apply(Roo.bootstrap.TabGroup, {
22580 * register a Navigation Group
22581 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22583 register : function(navgrp)
22585 this.groups[navgrp.navId] = navgrp;
22589 * fetch a Navigation Group based on the navigation ID
22590 * if one does not exist , it will get created.
22591 * @param {string} the navgroup to add
22592 * @returns {Roo.bootstrap.nav.Group} the navgroup
22594 get: function(navId) {
22595 if (typeof(this.groups[navId]) == 'undefined') {
22596 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22598 return this.groups[navId] ;
22613 * @class Roo.bootstrap.TabPanel
22614 * @extends Roo.bootstrap.Component
22615 * @children Roo.bootstrap.Component
22616 * Bootstrap TabPanel class
22617 * @cfg {Boolean} active panel active
22618 * @cfg {String} html panel content
22619 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22620 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22621 * @cfg {String} href click to link..
22622 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22626 * Create a new TabPanel
22627 * @param {Object} config The config object
22630 Roo.bootstrap.TabPanel = function(config){
22631 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22635 * Fires when the active status changes
22636 * @param {Roo.bootstrap.TabPanel} this
22637 * @param {Boolean} state the new state
22642 * @event beforedeactivate
22643 * Fires before a tab is de-activated - can be used to do validation on a form.
22644 * @param {Roo.bootstrap.TabPanel} this
22645 * @return {Boolean} false if there is an error
22648 'beforedeactivate': true
22651 this.tabId = this.tabId || Roo.id();
22655 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22662 touchSlide : false,
22663 getAutoCreate : function(){
22668 // item is needed for carousel - not sure if it has any effect otherwise
22669 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22670 html: this.html || ''
22674 cfg.cls += ' active';
22678 cfg.tabId = this.tabId;
22686 initEvents: function()
22688 var p = this.parent();
22690 this.navId = this.navId || p.navId;
22692 if (typeof(this.navId) != 'undefined') {
22693 // not really needed.. but just in case.. parent should be a NavGroup.
22694 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22698 var i = tg.tabs.length - 1;
22700 if(this.active && tg.bullets > 0 && i < tg.bullets){
22701 tg.setActiveBullet(i);
22705 this.el.on('click', this.onClick, this);
22707 if(Roo.isTouch && this.touchSlide){
22708 this.el.on("touchstart", this.onTouchStart, this);
22709 this.el.on("touchmove", this.onTouchMove, this);
22710 this.el.on("touchend", this.onTouchEnd, this);
22715 onRender : function(ct, position)
22717 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22720 setActive : function(state)
22722 Roo.log("panel - set active " + this.tabId + "=" + state);
22724 this.active = state;
22726 this.el.removeClass('active');
22728 } else if (!this.el.hasClass('active')) {
22729 this.el.addClass('active');
22732 this.fireEvent('changed', this, state);
22735 onClick : function(e)
22737 e.preventDefault();
22739 if(!this.href.length){
22743 window.location.href = this.href;
22752 onTouchStart : function(e)
22754 this.swiping = false;
22756 this.startX = e.browserEvent.touches[0].clientX;
22757 this.startY = e.browserEvent.touches[0].clientY;
22760 onTouchMove : function(e)
22762 this.swiping = true;
22764 this.endX = e.browserEvent.touches[0].clientX;
22765 this.endY = e.browserEvent.touches[0].clientY;
22768 onTouchEnd : function(e)
22775 var tabGroup = this.parent();
22777 if(this.endX > this.startX){ // swiping right
22778 tabGroup.showPanelPrev();
22782 if(this.startX > this.endX){ // swiping left
22783 tabGroup.showPanelNext();
22802 * @class Roo.bootstrap.form.DateField
22803 * @extends Roo.bootstrap.form.Input
22804 * Bootstrap DateField class
22805 * @cfg {Number} weekStart default 0
22806 * @cfg {String} viewMode default empty, (months|years)
22807 * @cfg {String} minViewMode default empty, (months|years)
22808 * @cfg {Number} startDate default -Infinity
22809 * @cfg {Number} endDate default Infinity
22810 * @cfg {Boolean} todayHighlight default false
22811 * @cfg {Boolean} todayBtn default false
22812 * @cfg {Boolean} calendarWeeks default false
22813 * @cfg {Object} daysOfWeekDisabled default empty
22814 * @cfg {Boolean} singleMode default false (true | false)
22816 * @cfg {Boolean} keyboardNavigation default true
22817 * @cfg {String} language default en
22820 * Create a new DateField
22821 * @param {Object} config The config object
22824 Roo.bootstrap.form.DateField = function(config){
22825 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22829 * Fires when this field show.
22830 * @param {Roo.bootstrap.form.DateField} this
22831 * @param {Mixed} date The date value
22836 * Fires when this field hide.
22837 * @param {Roo.bootstrap.form.DateField} this
22838 * @param {Mixed} date The date value
22843 * Fires when select a date.
22844 * @param {Roo.bootstrap.form.DateField} this
22845 * @param {Mixed} date The date value
22849 * @event beforeselect
22850 * Fires when before select a date.
22851 * @param {Roo.bootstrap.form.DateField} this
22852 * @param {Mixed} date The date value
22854 beforeselect : true
22858 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22861 * @cfg {String} format
22862 * The default date format string which can be overriden for localization support. The format must be
22863 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22867 * @cfg {String} altFormats
22868 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22869 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22871 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22879 todayHighlight : false,
22885 keyboardNavigation: true,
22887 calendarWeeks: false,
22889 startDate: -Infinity,
22893 daysOfWeekDisabled: [],
22897 singleMode : false,
22899 UTCDate: function()
22901 return new Date(Date.UTC.apply(Date, arguments));
22904 UTCToday: function()
22906 var today = new Date();
22907 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22910 getDate: function() {
22911 var d = this.getUTCDate();
22912 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22915 getUTCDate: function() {
22919 setDate: function(d) {
22920 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22923 setUTCDate: function(d) {
22925 this.setValue(this.formatDate(this.date));
22928 onRender: function(ct, position)
22931 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22933 this.language = this.language || 'en';
22934 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22935 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22937 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22938 this.format = this.format || 'm/d/y';
22939 this.isInline = false;
22940 this.isInput = true;
22941 this.component = this.el.select('.add-on', true).first() || false;
22942 this.component = (this.component && this.component.length === 0) ? false : this.component;
22943 this.hasInput = this.component && this.inputEl().length;
22945 if (typeof(this.minViewMode === 'string')) {
22946 switch (this.minViewMode) {
22948 this.minViewMode = 1;
22951 this.minViewMode = 2;
22954 this.minViewMode = 0;
22959 if (typeof(this.viewMode === 'string')) {
22960 switch (this.viewMode) {
22973 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22975 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22977 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22979 this.picker().on('mousedown', this.onMousedown, this);
22980 this.picker().on('click', this.onClick, this);
22982 this.picker().addClass('datepicker-dropdown');
22984 this.startViewMode = this.viewMode;
22986 if(this.singleMode){
22987 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22988 v.setVisibilityMode(Roo.Element.DISPLAY);
22992 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22993 v.setStyle('width', '189px');
22997 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22998 if(!this.calendarWeeks){
23003 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23004 v.attr('colspan', function(i, val){
23005 return parseInt(val) + 1;
23010 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23012 this.setStartDate(this.startDate);
23013 this.setEndDate(this.endDate);
23015 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23022 if(this.isInline) {
23027 picker : function()
23029 return this.pickerEl;
23030 // return this.el.select('.datepicker', true).first();
23033 fillDow: function()
23035 var dowCnt = this.weekStart;
23044 if(this.calendarWeeks){
23052 while (dowCnt < this.weekStart + 7) {
23056 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23060 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23063 fillMonths: function()
23066 var months = this.picker().select('>.datepicker-months td', true).first();
23068 months.dom.innerHTML = '';
23074 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23077 months.createChild(month);
23084 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;
23086 if (this.date < this.startDate) {
23087 this.viewDate = new Date(this.startDate);
23088 } else if (this.date > this.endDate) {
23089 this.viewDate = new Date(this.endDate);
23091 this.viewDate = new Date(this.date);
23099 var d = new Date(this.viewDate),
23100 year = d.getUTCFullYear(),
23101 month = d.getUTCMonth(),
23102 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23103 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23104 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23105 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23106 currentDate = this.date && this.date.valueOf(),
23107 today = this.UTCToday();
23109 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23111 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23113 // this.picker.select('>tfoot th.today').
23114 // .text(dates[this.language].today)
23115 // .toggle(this.todayBtn !== false);
23117 this.updateNavArrows();
23120 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23122 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23124 prevMonth.setUTCDate(day);
23126 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23128 var nextMonth = new Date(prevMonth);
23130 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23132 nextMonth = nextMonth.valueOf();
23134 var fillMonths = false;
23136 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23138 while(prevMonth.valueOf() <= nextMonth) {
23141 if (prevMonth.getUTCDay() === this.weekStart) {
23143 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23151 if(this.calendarWeeks){
23152 // ISO 8601: First week contains first thursday.
23153 // ISO also states week starts on Monday, but we can be more abstract here.
23155 // Start of current week: based on weekstart/current date
23156 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23157 // Thursday of this week
23158 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23159 // First Thursday of year, year from thursday
23160 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23161 // Calendar week: ms between thursdays, div ms per day, div 7 days
23162 calWeek = (th - yth) / 864e5 / 7 + 1;
23164 fillMonths.cn.push({
23172 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23174 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23177 if (this.todayHighlight &&
23178 prevMonth.getUTCFullYear() == today.getFullYear() &&
23179 prevMonth.getUTCMonth() == today.getMonth() &&
23180 prevMonth.getUTCDate() == today.getDate()) {
23181 clsName += ' today';
23184 if (currentDate && prevMonth.valueOf() === currentDate) {
23185 clsName += ' active';
23188 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23189 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23190 clsName += ' disabled';
23193 fillMonths.cn.push({
23195 cls: 'day ' + clsName,
23196 html: prevMonth.getDate()
23199 prevMonth.setDate(prevMonth.getDate()+1);
23202 var currentYear = this.date && this.date.getUTCFullYear();
23203 var currentMonth = this.date && this.date.getUTCMonth();
23205 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23207 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23208 v.removeClass('active');
23210 if(currentYear === year && k === currentMonth){
23211 v.addClass('active');
23214 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23215 v.addClass('disabled');
23221 year = parseInt(year/10, 10) * 10;
23223 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23225 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23228 for (var i = -1; i < 11; i++) {
23229 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23231 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23239 showMode: function(dir)
23242 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23245 Roo.each(this.picker().select('>div',true).elements, function(v){
23246 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23249 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23254 if(this.isInline) {
23258 this.picker().removeClass(['bottom', 'top']);
23260 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23262 * place to the top of element!
23266 this.picker().addClass('top');
23267 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23272 this.picker().addClass('bottom');
23274 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23277 parseDate : function(value)
23279 if(!value || value instanceof Date){
23282 var v = Date.parseDate(value, this.format);
23283 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23284 v = Date.parseDate(value, 'Y-m-d');
23286 if(!v && this.altFormats){
23287 if(!this.altFormatsArray){
23288 this.altFormatsArray = this.altFormats.split("|");
23290 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23291 v = Date.parseDate(value, this.altFormatsArray[i]);
23297 formatDate : function(date, fmt)
23299 return (!date || !(date instanceof Date)) ?
23300 date : date.dateFormat(fmt || this.format);
23303 onFocus : function()
23305 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23309 onBlur : function()
23311 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23313 var d = this.inputEl().getValue();
23320 showPopup : function()
23322 this.picker().show();
23326 this.fireEvent('showpopup', this, this.date);
23329 hidePopup : function()
23331 if(this.isInline) {
23334 this.picker().hide();
23335 this.viewMode = this.startViewMode;
23338 this.fireEvent('hidepopup', this, this.date);
23342 onMousedown: function(e)
23344 e.stopPropagation();
23345 e.preventDefault();
23350 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23354 setValue: function(v)
23356 if(this.fireEvent('beforeselect', this, v) !== false){
23357 var d = new Date(this.parseDate(v) ).clearTime();
23359 if(isNaN(d.getTime())){
23360 this.date = this.viewDate = '';
23361 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23365 v = this.formatDate(d);
23367 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23369 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23373 this.fireEvent('select', this, this.date);
23377 getValue: function()
23379 return this.formatDate(this.date);
23382 fireKey: function(e)
23384 if (!this.picker().isVisible()){
23385 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23391 var dateChanged = false,
23393 newDate, newViewDate;
23398 e.preventDefault();
23402 if (!this.keyboardNavigation) {
23405 dir = e.keyCode == 37 ? -1 : 1;
23408 newDate = this.moveYear(this.date, dir);
23409 newViewDate = this.moveYear(this.viewDate, dir);
23410 } else if (e.shiftKey){
23411 newDate = this.moveMonth(this.date, dir);
23412 newViewDate = this.moveMonth(this.viewDate, dir);
23414 newDate = new Date(this.date);
23415 newDate.setUTCDate(this.date.getUTCDate() + dir);
23416 newViewDate = new Date(this.viewDate);
23417 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23419 if (this.dateWithinRange(newDate)){
23420 this.date = newDate;
23421 this.viewDate = newViewDate;
23422 this.setValue(this.formatDate(this.date));
23424 e.preventDefault();
23425 dateChanged = true;
23430 if (!this.keyboardNavigation) {
23433 dir = e.keyCode == 38 ? -1 : 1;
23435 newDate = this.moveYear(this.date, dir);
23436 newViewDate = this.moveYear(this.viewDate, dir);
23437 } else if (e.shiftKey){
23438 newDate = this.moveMonth(this.date, dir);
23439 newViewDate = this.moveMonth(this.viewDate, dir);
23441 newDate = new Date(this.date);
23442 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23443 newViewDate = new Date(this.viewDate);
23444 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23446 if (this.dateWithinRange(newDate)){
23447 this.date = newDate;
23448 this.viewDate = newViewDate;
23449 this.setValue(this.formatDate(this.date));
23451 e.preventDefault();
23452 dateChanged = true;
23456 this.setValue(this.formatDate(this.date));
23458 e.preventDefault();
23461 this.setValue(this.formatDate(this.date));
23475 onClick: function(e)
23477 e.stopPropagation();
23478 e.preventDefault();
23480 var target = e.getTarget();
23482 if(target.nodeName.toLowerCase() === 'i'){
23483 target = Roo.get(target).dom.parentNode;
23486 var nodeName = target.nodeName;
23487 var className = target.className;
23488 var html = target.innerHTML;
23489 //Roo.log(nodeName);
23491 switch(nodeName.toLowerCase()) {
23493 switch(className) {
23499 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23500 switch(this.viewMode){
23502 this.viewDate = this.moveMonth(this.viewDate, dir);
23506 this.viewDate = this.moveYear(this.viewDate, dir);
23512 var date = new Date();
23513 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23515 this.setValue(this.formatDate(this.date));
23522 if (className.indexOf('disabled') < 0) {
23523 if (!this.viewDate) {
23524 this.viewDate = new Date();
23526 this.viewDate.setUTCDate(1);
23527 if (className.indexOf('month') > -1) {
23528 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23530 var year = parseInt(html, 10) || 0;
23531 this.viewDate.setUTCFullYear(year);
23535 if(this.singleMode){
23536 this.setValue(this.formatDate(this.viewDate));
23547 //Roo.log(className);
23548 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23549 var day = parseInt(html, 10) || 1;
23550 var year = (this.viewDate || new Date()).getUTCFullYear(),
23551 month = (this.viewDate || new Date()).getUTCMonth();
23553 if (className.indexOf('old') > -1) {
23560 } else if (className.indexOf('new') > -1) {
23568 //Roo.log([year,month,day]);
23569 this.date = this.UTCDate(year, month, day,0,0,0,0);
23570 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23572 //Roo.log(this.formatDate(this.date));
23573 this.setValue(this.formatDate(this.date));
23580 setStartDate: function(startDate)
23582 this.startDate = startDate || -Infinity;
23583 if (this.startDate !== -Infinity) {
23584 this.startDate = this.parseDate(this.startDate);
23587 this.updateNavArrows();
23590 setEndDate: function(endDate)
23592 this.endDate = endDate || Infinity;
23593 if (this.endDate !== Infinity) {
23594 this.endDate = this.parseDate(this.endDate);
23597 this.updateNavArrows();
23600 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23602 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23603 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23604 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23606 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23607 return parseInt(d, 10);
23610 this.updateNavArrows();
23613 updateNavArrows: function()
23615 if(this.singleMode){
23619 var d = new Date(this.viewDate),
23620 year = d.getUTCFullYear(),
23621 month = d.getUTCMonth();
23623 Roo.each(this.picker().select('.prev', true).elements, function(v){
23625 switch (this.viewMode) {
23628 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23634 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23641 Roo.each(this.picker().select('.next', true).elements, function(v){
23643 switch (this.viewMode) {
23646 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23652 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23660 moveMonth: function(date, dir)
23665 var new_date = new Date(date.valueOf()),
23666 day = new_date.getUTCDate(),
23667 month = new_date.getUTCMonth(),
23668 mag = Math.abs(dir),
23670 dir = dir > 0 ? 1 : -1;
23673 // If going back one month, make sure month is not current month
23674 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23676 return new_date.getUTCMonth() == month;
23678 // If going forward one month, make sure month is as expected
23679 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23681 return new_date.getUTCMonth() != new_month;
23683 new_month = month + dir;
23684 new_date.setUTCMonth(new_month);
23685 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23686 if (new_month < 0 || new_month > 11) {
23687 new_month = (new_month + 12) % 12;
23690 // For magnitudes >1, move one month at a time...
23691 for (var i=0; i<mag; i++) {
23692 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23693 new_date = this.moveMonth(new_date, dir);
23695 // ...then reset the day, keeping it in the new month
23696 new_month = new_date.getUTCMonth();
23697 new_date.setUTCDate(day);
23699 return new_month != new_date.getUTCMonth();
23702 // Common date-resetting loop -- if date is beyond end of month, make it
23705 new_date.setUTCDate(--day);
23706 new_date.setUTCMonth(new_month);
23711 moveYear: function(date, dir)
23713 return this.moveMonth(date, dir*12);
23716 dateWithinRange: function(date)
23718 return date >= this.startDate && date <= this.endDate;
23724 this.picker().remove();
23727 validateValue : function(value)
23729 if(this.getVisibilityEl().hasClass('hidden')){
23733 if(value.length < 1) {
23734 if(this.allowBlank){
23740 if(value.length < this.minLength){
23743 if(value.length > this.maxLength){
23747 var vt = Roo.form.VTypes;
23748 if(!vt[this.vtype](value, this)){
23752 if(typeof this.validator == "function"){
23753 var msg = this.validator(value);
23759 if(this.regex && !this.regex.test(value)){
23763 if(typeof(this.parseDate(value)) == 'undefined'){
23767 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23771 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23781 this.date = this.viewDate = '';
23783 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23788 Roo.apply(Roo.bootstrap.form.DateField, {
23799 html: '<i class="fa fa-arrow-left"/>'
23809 html: '<i class="fa fa-arrow-right"/>'
23851 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23852 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23853 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23854 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23855 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23868 navFnc: 'FullYear',
23873 navFnc: 'FullYear',
23878 Roo.apply(Roo.bootstrap.form.DateField, {
23882 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23886 cls: 'datepicker-days',
23890 cls: 'table-condensed',
23892 Roo.bootstrap.form.DateField.head,
23896 Roo.bootstrap.form.DateField.footer
23903 cls: 'datepicker-months',
23907 cls: 'table-condensed',
23909 Roo.bootstrap.form.DateField.head,
23910 Roo.bootstrap.form.DateField.content,
23911 Roo.bootstrap.form.DateField.footer
23918 cls: 'datepicker-years',
23922 cls: 'table-condensed',
23924 Roo.bootstrap.form.DateField.head,
23925 Roo.bootstrap.form.DateField.content,
23926 Roo.bootstrap.form.DateField.footer
23945 * @class Roo.bootstrap.form.TimeField
23946 * @extends Roo.bootstrap.form.Input
23947 * Bootstrap DateField class
23948 * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23952 * Create a new TimeField
23953 * @param {Object} config The config object
23956 Roo.bootstrap.form.TimeField = function(config){
23957 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23961 * Fires when this field show.
23962 * @param {Roo.bootstrap.form.DateField} thisthis
23963 * @param {Mixed} date The date value
23968 * Fires when this field hide.
23969 * @param {Roo.bootstrap.form.DateField} this
23970 * @param {Mixed} date The date value
23975 * Fires when select a date.
23976 * @param {Roo.bootstrap.form.DateField} this
23977 * @param {Mixed} date The date value
23983 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23986 * @cfg {String} format
23987 * The default time format string which can be overriden for localization support. The format must be
23988 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23993 getAutoCreate : function()
23995 this.after = '<i class="fa far fa-clock"></i>';
23996 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24000 onRender: function(ct, position)
24003 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24005 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24007 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24009 this.pop = this.picker().select('>.datepicker-time',true).first();
24010 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24012 this.picker().on('mousedown', this.onMousedown, this);
24013 this.picker().on('click', this.onClick, this);
24015 this.picker().addClass('datepicker-dropdown');
24020 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24021 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24022 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24023 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24024 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24025 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24029 fireKey: function(e){
24030 if (!this.picker().isVisible()){
24031 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24037 e.preventDefault();
24045 this.onTogglePeriod();
24048 this.onIncrementMinutes();
24051 this.onDecrementMinutes();
24060 onClick: function(e) {
24061 e.stopPropagation();
24062 e.preventDefault();
24065 picker : function()
24067 return this.pickerEl;
24070 fillTime: function()
24072 var time = this.pop.select('tbody', true).first();
24074 time.dom.innerHTML = '';
24089 cls: 'hours-up fa fas fa-chevron-up'
24109 cls: 'minutes-up fa fas fa-chevron-up'
24130 cls: 'timepicker-hour',
24145 cls: 'timepicker-minute',
24160 cls: 'btn btn-primary period',
24182 cls: 'hours-down fa fas fa-chevron-down'
24202 cls: 'minutes-down fa fas fa-chevron-down'
24219 // default minute is a multiple of minuteStep
24220 if(typeof(this.time) === 'undefined') {
24221 this.time = new Date();
24222 this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24224 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24231 var hours = this.time.getHours();
24232 var minutes = this.time.getMinutes();
24245 hours = hours - 12;
24249 hours = '0' + hours;
24253 minutes = '0' + minutes;
24256 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24257 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24258 this.pop.select('button', true).first().dom.innerHTML = period;
24264 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24266 var cls = ['bottom'];
24268 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24275 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24279 //this.picker().setXY(20000,20000);
24280 this.picker().addClass(cls.join('-'));
24284 Roo.each(cls, function(c){
24289 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24290 //_this.picker().setTop(_this.inputEl().getHeight());
24294 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24296 //_this.picker().setTop(0 - _this.picker().getHeight());
24301 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24305 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24313 onFocus : function()
24315 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24319 onBlur : function()
24321 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24327 this.picker().show();
24332 this.fireEvent('show', this, this.date);
24337 this.picker().hide();
24340 this.fireEvent('hide', this, this.date);
24343 setTime : function()
24346 this.setValue(this.time.format(this.format));
24348 this.fireEvent('select', this, this.date);
24353 onMousedown: function(e){
24354 e.stopPropagation();
24355 e.preventDefault();
24358 onIncrementHours: function()
24360 Roo.log('onIncrementHours');
24361 this.time = this.time.add(Date.HOUR, 1);
24366 onDecrementHours: function()
24368 Roo.log('onDecrementHours');
24369 this.time = this.time.add(Date.HOUR, -1);
24373 onIncrementMinutes: function()
24375 Roo.log('onIncrementMinutes');
24376 var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24377 this.time = this.time.add(Date.MINUTE, minutesToAdd);
24381 onDecrementMinutes: function()
24383 Roo.log('onDecrementMinutes');
24384 var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24385 this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24389 onTogglePeriod: function()
24391 Roo.log('onTogglePeriod');
24392 this.time = this.time.add(Date.HOUR, 12);
24400 Roo.apply(Roo.bootstrap.form.TimeField, {
24404 cls: 'datepicker dropdown-menu',
24408 cls: 'datepicker-time',
24412 cls: 'table-condensed',
24441 cls: 'btn btn-info ok',
24469 * @class Roo.bootstrap.form.MonthField
24470 * @extends Roo.bootstrap.form.Input
24471 * Bootstrap MonthField class
24473 * @cfg {String} language default en
24476 * Create a new MonthField
24477 * @param {Object} config The config object
24480 Roo.bootstrap.form.MonthField = function(config){
24481 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24486 * Fires when this field show.
24487 * @param {Roo.bootstrap.form.MonthField} this
24488 * @param {Mixed} date The date value
24493 * Fires when this field hide.
24494 * @param {Roo.bootstrap.form.MonthField} this
24495 * @param {Mixed} date The date value
24500 * Fires when select a date.
24501 * @param {Roo.bootstrap.form.MonthField} this
24502 * @param {String} oldvalue The old value
24503 * @param {String} newvalue The new value
24509 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24511 onRender: function(ct, position)
24514 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24516 this.language = this.language || 'en';
24517 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24518 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24520 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24521 this.isInline = false;
24522 this.isInput = true;
24523 this.component = this.el.select('.add-on', true).first() || false;
24524 this.component = (this.component && this.component.length === 0) ? false : this.component;
24525 this.hasInput = this.component && this.inputEL().length;
24527 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24529 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24531 this.picker().on('mousedown', this.onMousedown, this);
24532 this.picker().on('click', this.onClick, this);
24534 this.picker().addClass('datepicker-dropdown');
24536 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24537 v.setStyle('width', '189px');
24544 if(this.isInline) {
24550 setValue: function(v, suppressEvent)
24552 var o = this.getValue();
24554 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24558 if(suppressEvent !== true){
24559 this.fireEvent('select', this, o, v);
24564 getValue: function()
24569 onClick: function(e)
24571 e.stopPropagation();
24572 e.preventDefault();
24574 var target = e.getTarget();
24576 if(target.nodeName.toLowerCase() === 'i'){
24577 target = Roo.get(target).dom.parentNode;
24580 var nodeName = target.nodeName;
24581 var className = target.className;
24582 var html = target.innerHTML;
24584 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24588 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24590 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24596 picker : function()
24598 return this.pickerEl;
24601 fillMonths: function()
24604 var months = this.picker().select('>.datepicker-months td', true).first();
24606 months.dom.innerHTML = '';
24612 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24615 months.createChild(month);
24624 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24625 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24628 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24629 e.removeClass('active');
24631 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24632 e.addClass('active');
24639 if(this.isInline) {
24643 this.picker().removeClass(['bottom', 'top']);
24645 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24647 * place to the top of element!
24651 this.picker().addClass('top');
24652 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24657 this.picker().addClass('bottom');
24659 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24662 onFocus : function()
24664 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24668 onBlur : function()
24670 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24672 var d = this.inputEl().getValue();
24681 this.picker().show();
24682 this.picker().select('>.datepicker-months', true).first().show();
24686 this.fireEvent('show', this, this.date);
24691 if(this.isInline) {
24694 this.picker().hide();
24695 this.fireEvent('hide', this, this.date);
24699 onMousedown: function(e)
24701 e.stopPropagation();
24702 e.preventDefault();
24707 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24711 fireKey: function(e)
24713 if (!this.picker().isVisible()){
24714 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24725 e.preventDefault();
24729 dir = e.keyCode == 37 ? -1 : 1;
24731 this.vIndex = this.vIndex + dir;
24733 if(this.vIndex < 0){
24737 if(this.vIndex > 11){
24741 if(isNaN(this.vIndex)){
24745 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24751 dir = e.keyCode == 38 ? -1 : 1;
24753 this.vIndex = this.vIndex + dir * 4;
24755 if(this.vIndex < 0){
24759 if(this.vIndex > 11){
24763 if(isNaN(this.vIndex)){
24767 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24772 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24773 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24777 e.preventDefault();
24780 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24781 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24797 this.picker().remove();
24802 Roo.apply(Roo.bootstrap.form.MonthField, {
24821 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24822 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24827 Roo.apply(Roo.bootstrap.form.MonthField, {
24831 cls: 'datepicker dropdown-menu roo-dynamic',
24835 cls: 'datepicker-months',
24839 cls: 'table-condensed',
24841 Roo.bootstrap.form.DateField.content
24861 * @class Roo.bootstrap.form.CheckBox
24862 * @extends Roo.bootstrap.form.Input
24863 * Bootstrap CheckBox class
24865 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24866 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24867 * @cfg {String} boxLabel The text that appears beside the checkbox
24868 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24869 * @cfg {Boolean} checked initnal the element
24870 * @cfg {Boolean} inline inline the element (default false)
24871 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24872 * @cfg {String} tooltip label tooltip
24875 * Create a new CheckBox
24876 * @param {Object} config The config object
24879 Roo.bootstrap.form.CheckBox = function(config){
24880 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24885 * Fires when the element is checked or unchecked.
24886 * @param {Roo.bootstrap.form.CheckBox} this This input
24887 * @param {Boolean} checked The new checked value
24892 * Fires when the element is click.
24893 * @param {Roo.bootstrap.form.CheckBox} this This input
24900 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24902 inputType: 'checkbox',
24911 // checkbox success does not make any sense really..
24916 getAutoCreate : function()
24918 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24924 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24927 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24933 type : this.inputType,
24934 value : this.inputValue,
24935 cls : 'roo-' + this.inputType, //'form-box',
24936 placeholder : this.placeholder || ''
24940 if(this.inputType != 'radio'){
24944 cls : 'roo-hidden-value',
24945 value : this.checked ? this.inputValue : this.valueOff
24950 if (this.weight) { // Validity check?
24951 cfg.cls += " " + this.inputType + "-" + this.weight;
24954 if (this.disabled) {
24955 input.disabled=true;
24959 input.checked = this.checked;
24964 input.name = this.name;
24966 if(this.inputType != 'radio'){
24967 hidden.name = this.name;
24968 input.name = '_hidden_' + this.name;
24973 input.cls += ' input-' + this.size;
24978 ['xs','sm','md','lg'].map(function(size){
24979 if (settings[size]) {
24980 cfg.cls += ' col-' + size + '-' + settings[size];
24984 var inputblock = input;
24986 if (this.before || this.after) {
24989 cls : 'input-group',
24994 inputblock.cn.push({
24996 cls : 'input-group-addon',
25001 inputblock.cn.push(input);
25003 if(this.inputType != 'radio'){
25004 inputblock.cn.push(hidden);
25008 inputblock.cn.push({
25010 cls : 'input-group-addon',
25016 var boxLabelCfg = false;
25022 //'for': id, // box label is handled by onclick - so no for...
25024 html: this.boxLabel
25027 boxLabelCfg.tooltip = this.tooltip;
25033 if (align ==='left' && this.fieldLabel.length) {
25034 // Roo.log("left and has label");
25039 cls : 'control-label',
25040 html : this.fieldLabel
25051 cfg.cn[1].cn.push(boxLabelCfg);
25054 if(this.labelWidth > 12){
25055 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25058 if(this.labelWidth < 13 && this.labelmd == 0){
25059 this.labelmd = this.labelWidth;
25062 if(this.labellg > 0){
25063 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25064 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25067 if(this.labelmd > 0){
25068 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25069 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25072 if(this.labelsm > 0){
25073 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25074 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25077 if(this.labelxs > 0){
25078 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25079 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25082 } else if ( this.fieldLabel.length) {
25083 // Roo.log(" label");
25087 tag: this.boxLabel ? 'span' : 'label',
25089 cls: 'control-label box-input-label',
25090 //cls : 'input-group-addon',
25091 html : this.fieldLabel
25098 cfg.cn.push(boxLabelCfg);
25103 // Roo.log(" no label && no align");
25104 cfg.cn = [ inputblock ] ;
25106 cfg.cn.push(boxLabelCfg);
25114 if(this.inputType != 'radio'){
25115 cfg.cn.push(hidden);
25123 * return the real input element.
25125 inputEl: function ()
25127 return this.el.select('input.roo-' + this.inputType,true).first();
25129 hiddenEl: function ()
25131 return this.el.select('input.roo-hidden-value',true).first();
25134 labelEl: function()
25136 return this.el.select('label.control-label',true).first();
25138 /* depricated... */
25142 return this.labelEl();
25145 boxLabelEl: function()
25147 return this.el.select('label.box-label',true).first();
25150 initEvents : function()
25152 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25154 this.inputEl().on('click', this.onClick, this);
25156 if (this.boxLabel) {
25157 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25160 this.startValue = this.getValue();
25163 Roo.bootstrap.form.CheckBox.register(this);
25167 onClick : function(e)
25169 if(this.fireEvent('click', this, e) !== false){
25170 this.setChecked(!this.checked);
25175 setChecked : function(state,suppressEvent)
25177 this.startValue = this.getValue();
25179 if(this.inputType == 'radio'){
25181 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25182 e.dom.checked = false;
25185 this.inputEl().dom.checked = true;
25187 this.inputEl().dom.value = this.inputValue;
25189 if(suppressEvent !== true){
25190 this.fireEvent('check', this, true);
25198 this.checked = state;
25200 this.inputEl().dom.checked = state;
25203 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25205 if(suppressEvent !== true){
25206 this.fireEvent('check', this, state);
25212 getValue : function()
25214 if(this.inputType == 'radio'){
25215 return this.getGroupValue();
25218 return this.hiddenEl().dom.value;
25222 getGroupValue : function()
25224 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25228 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25231 setValue : function(v,suppressEvent)
25233 if(this.inputType == 'radio'){
25234 this.setGroupValue(v, suppressEvent);
25238 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25243 setGroupValue : function(v, suppressEvent)
25245 this.startValue = this.getValue();
25247 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25248 e.dom.checked = false;
25250 if(e.dom.value == v){
25251 e.dom.checked = true;
25255 if(suppressEvent !== true){
25256 this.fireEvent('check', this, true);
25264 validate : function()
25266 if(this.getVisibilityEl().hasClass('hidden')){
25272 (this.inputType == 'radio' && this.validateRadio()) ||
25273 (this.inputType == 'checkbox' && this.validateCheckbox())
25279 this.markInvalid();
25283 validateRadio : function()
25285 if(this.getVisibilityEl().hasClass('hidden')){
25289 if(this.allowBlank){
25295 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25296 if(!e.dom.checked){
25308 validateCheckbox : function()
25311 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25312 //return (this.getValue() == this.inputValue) ? true : false;
25315 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25323 for(var i in group){
25324 if(group[i].el.isVisible(true)){
25332 for(var i in group){
25337 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25344 * Mark this field as valid
25346 markValid : function()
25350 this.fireEvent('valid', this);
25352 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25355 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25362 if(this.inputType == 'radio'){
25363 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25364 var fg = e.findParent('.form-group', false, true);
25365 if (Roo.bootstrap.version == 3) {
25366 fg.removeClass([_this.invalidClass, _this.validClass]);
25367 fg.addClass(_this.validClass);
25369 fg.removeClass(['is-valid', 'is-invalid']);
25370 fg.addClass('is-valid');
25378 var fg = this.el.findParent('.form-group', false, true);
25379 if (Roo.bootstrap.version == 3) {
25380 fg.removeClass([this.invalidClass, this.validClass]);
25381 fg.addClass(this.validClass);
25383 fg.removeClass(['is-valid', 'is-invalid']);
25384 fg.addClass('is-valid');
25389 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25395 for(var i in group){
25396 var fg = group[i].el.findParent('.form-group', false, true);
25397 if (Roo.bootstrap.version == 3) {
25398 fg.removeClass([this.invalidClass, this.validClass]);
25399 fg.addClass(this.validClass);
25401 fg.removeClass(['is-valid', 'is-invalid']);
25402 fg.addClass('is-valid');
25408 * Mark this field as invalid
25409 * @param {String} msg The validation message
25411 markInvalid : function(msg)
25413 if(this.allowBlank){
25419 this.fireEvent('invalid', this, msg);
25421 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25424 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25428 label.markInvalid();
25431 if(this.inputType == 'radio'){
25433 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25434 var fg = e.findParent('.form-group', false, true);
25435 if (Roo.bootstrap.version == 3) {
25436 fg.removeClass([_this.invalidClass, _this.validClass]);
25437 fg.addClass(_this.invalidClass);
25439 fg.removeClass(['is-invalid', 'is-valid']);
25440 fg.addClass('is-invalid');
25448 var fg = this.el.findParent('.form-group', false, true);
25449 if (Roo.bootstrap.version == 3) {
25450 fg.removeClass([_this.invalidClass, _this.validClass]);
25451 fg.addClass(_this.invalidClass);
25453 fg.removeClass(['is-invalid', 'is-valid']);
25454 fg.addClass('is-invalid');
25459 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25465 for(var i in group){
25466 var fg = group[i].el.findParent('.form-group', false, true);
25467 if (Roo.bootstrap.version == 3) {
25468 fg.removeClass([_this.invalidClass, _this.validClass]);
25469 fg.addClass(_this.invalidClass);
25471 fg.removeClass(['is-invalid', 'is-valid']);
25472 fg.addClass('is-invalid');
25478 clearInvalid : function()
25480 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25482 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25484 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25486 if (label && label.iconEl) {
25487 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25488 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25492 disable : function()
25494 if(this.inputType != 'radio'){
25495 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25502 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25503 _this.getActionEl().addClass(this.disabledClass);
25504 e.dom.disabled = true;
25508 this.disabled = true;
25509 this.fireEvent("disable", this);
25513 enable : function()
25515 if(this.inputType != 'radio'){
25516 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25523 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25524 _this.getActionEl().removeClass(this.disabledClass);
25525 e.dom.disabled = false;
25529 this.disabled = false;
25530 this.fireEvent("enable", this);
25534 setBoxLabel : function(v)
25539 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25545 Roo.apply(Roo.bootstrap.form.CheckBox, {
25550 * register a CheckBox Group
25551 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25553 register : function(checkbox)
25555 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25556 this.groups[checkbox.groupId] = {};
25559 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25563 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25567 * fetch a CheckBox Group based on the group ID
25568 * @param {string} the group ID
25569 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25571 get: function(groupId) {
25572 if (typeof(this.groups[groupId]) == 'undefined') {
25576 return this.groups[groupId] ;
25589 * @class Roo.bootstrap.form.Radio
25590 * @extends Roo.bootstrap.Component
25591 * Bootstrap Radio class
25592 * @cfg {String} boxLabel - the label associated
25593 * @cfg {String} value - the value of radio
25596 * Create a new Radio
25597 * @param {Object} config The config object
25599 Roo.bootstrap.form.Radio = function(config){
25600 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25604 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25610 getAutoCreate : function()
25614 cls : 'form-group radio',
25619 html : this.boxLabel
25627 initEvents : function()
25629 this.parent().register(this);
25631 this.el.on('click', this.onClick, this);
25635 onClick : function(e)
25637 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25638 this.setChecked(true);
25642 setChecked : function(state, suppressEvent)
25644 this.parent().setValue(this.value, suppressEvent);
25648 setBoxLabel : function(v)
25653 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25668 * @class Roo.bootstrap.form.SecurePass
25669 * @extends Roo.bootstrap.form.Input
25670 * Bootstrap SecurePass class
25674 * Create a new SecurePass
25675 * @param {Object} config The config object
25678 Roo.bootstrap.form.SecurePass = function (config) {
25679 // these go here, so the translation tool can replace them..
25681 PwdEmpty: "Please type a password, and then retype it to confirm.",
25682 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25683 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25684 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25685 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25686 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25687 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25688 TooWeak: "Your password is Too Weak."
25690 this.meterLabel = "Password strength:";
25691 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25692 this.meterClass = [
25693 "roo-password-meter-tooweak",
25694 "roo-password-meter-weak",
25695 "roo-password-meter-medium",
25696 "roo-password-meter-strong",
25697 "roo-password-meter-grey"
25702 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25705 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25707 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25709 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25710 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25711 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25712 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25713 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25714 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25715 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25725 * @cfg {String/Object} Label for the strength meter (defaults to
25726 * 'Password strength:')
25731 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25732 * ['Weak', 'Medium', 'Strong'])
25735 pwdStrengths: false,
25748 initEvents: function ()
25750 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25752 if (this.el.is('input[type=password]') && Roo.isSafari) {
25753 this.el.on('keydown', this.SafariOnKeyDown, this);
25756 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25759 onRender: function (ct, position)
25761 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25762 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25763 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25765 this.trigger.createChild({
25770 cls: 'roo-password-meter-grey col-xs-12',
25773 //width: this.meterWidth + 'px'
25777 cls: 'roo-password-meter-text'
25783 if (this.hideTrigger) {
25784 this.trigger.setDisplayed(false);
25786 this.setSize(this.width || '', this.height || '');
25789 onDestroy: function ()
25791 if (this.trigger) {
25792 this.trigger.removeAllListeners();
25793 this.trigger.remove();
25796 this.wrap.remove();
25798 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25801 checkStrength: function ()
25803 var pwd = this.inputEl().getValue();
25804 if (pwd == this._lastPwd) {
25809 if (this.ClientSideStrongPassword(pwd)) {
25811 } else if (this.ClientSideMediumPassword(pwd)) {
25813 } else if (this.ClientSideWeakPassword(pwd)) {
25819 Roo.log('strength1: ' + strength);
25821 //var pm = this.trigger.child('div/div/div').dom;
25822 var pm = this.trigger.child('div/div');
25823 pm.removeClass(this.meterClass);
25824 pm.addClass(this.meterClass[strength]);
25827 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25829 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25831 this._lastPwd = pwd;
25835 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25837 this._lastPwd = '';
25839 var pm = this.trigger.child('div/div');
25840 pm.removeClass(this.meterClass);
25841 pm.addClass('roo-password-meter-grey');
25844 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25847 this.inputEl().dom.type='password';
25850 validateValue: function (value)
25852 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25855 if (value.length == 0) {
25856 if (this.allowBlank) {
25857 this.clearInvalid();
25861 this.markInvalid(this.errors.PwdEmpty);
25862 this.errorMsg = this.errors.PwdEmpty;
25870 if (!value.match(/[\x21-\x7e]+/)) {
25871 this.markInvalid(this.errors.PwdBadChar);
25872 this.errorMsg = this.errors.PwdBadChar;
25875 if (value.length < 6) {
25876 this.markInvalid(this.errors.PwdShort);
25877 this.errorMsg = this.errors.PwdShort;
25880 if (value.length > 16) {
25881 this.markInvalid(this.errors.PwdLong);
25882 this.errorMsg = this.errors.PwdLong;
25886 if (this.ClientSideStrongPassword(value)) {
25888 } else if (this.ClientSideMediumPassword(value)) {
25890 } else if (this.ClientSideWeakPassword(value)) {
25897 if (strength < 2) {
25898 //this.markInvalid(this.errors.TooWeak);
25899 this.errorMsg = this.errors.TooWeak;
25904 console.log('strength2: ' + strength);
25906 //var pm = this.trigger.child('div/div/div').dom;
25908 var pm = this.trigger.child('div/div');
25909 pm.removeClass(this.meterClass);
25910 pm.addClass(this.meterClass[strength]);
25912 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25914 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25916 this.errorMsg = '';
25920 CharacterSetChecks: function (type)
25923 this.fResult = false;
25926 isctype: function (character, type)
25929 case this.kCapitalLetter:
25930 if (character >= 'A' && character <= 'Z') {
25935 case this.kSmallLetter:
25936 if (character >= 'a' && character <= 'z') {
25942 if (character >= '0' && character <= '9') {
25947 case this.kPunctuation:
25948 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25959 IsLongEnough: function (pwd, size)
25961 return !(pwd == null || isNaN(size) || pwd.length < size);
25964 SpansEnoughCharacterSets: function (word, nb)
25966 if (!this.IsLongEnough(word, nb))
25971 var characterSetChecks = new Array(
25972 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25973 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25976 for (var index = 0; index < word.length; ++index) {
25977 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25978 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25979 characterSetChecks[nCharSet].fResult = true;
25986 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25987 if (characterSetChecks[nCharSet].fResult) {
25992 if (nCharSets < nb) {
25998 ClientSideStrongPassword: function (pwd)
26000 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26003 ClientSideMediumPassword: function (pwd)
26005 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26008 ClientSideWeakPassword: function (pwd)
26010 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26013 });Roo.rtf = {}; // namespace
26014 Roo.rtf.Hex = function(hex)
26018 Roo.rtf.Paragraph = function(opts)
26020 this.content = []; ///??? is that used?
26021 };Roo.rtf.Span = function(opts)
26023 this.value = opts.value;
26026 Roo.rtf.Group = function(parent)
26028 // we dont want to acutally store parent - it will make debug a nightmare..
26036 Roo.rtf.Group.prototype = {
26040 addContent : function(node) {
26041 // could set styles...
26042 this.content.push(node);
26044 addChild : function(cn)
26048 // only for images really...
26049 toDataURL : function()
26051 var mimetype = false;
26053 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
26054 mimetype = "image/png";
26056 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26057 mimetype = "image/jpeg";
26060 return 'about:blank'; // ?? error?
26064 var hexstring = this.content[this.content.length-1].value;
26066 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26067 return String.fromCharCode(parseInt(a, 16));
26072 // this looks like it's normally the {rtf{ .... }}
26073 Roo.rtf.Document = function()
26075 // we dont want to acutally store parent - it will make debug a nightmare..
26081 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
26082 addChild : function(cn)
26086 case 'rtlch': // most content seems to be inside this??
26089 this.rtlch.push(cn);
26092 this[cn.type] = cn;
26097 getElementsByType : function(type)
26100 this._getElementsByType(type, ret, this.cn, 'rtf');
26103 _getElementsByType : function (type, ret, search_array, path)
26105 search_array.forEach(function(n,i) {
26106 if (n.type == type) {
26107 n.path = path + '/' + n.type + ':' + i;
26110 if (n.cn.length > 0) {
26111 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26118 Roo.rtf.Ctrl = function(opts)
26120 this.value = opts.value;
26121 this.param = opts.param;
26126 * based on this https://github.com/iarna/rtf-parser
26127 * it's really only designed to extract pict from pasted RTF
26131 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26140 Roo.rtf.Parser = function(text) {
26141 //super({objectMode: true})
26143 this.parserState = this.parseText;
26145 // these are for interpeter...
26147 ///this.parserState = this.parseTop
26148 this.groupStack = [];
26149 this.hexStore = [];
26152 this.groups = []; // where we put the return.
26154 for (var ii = 0; ii < text.length; ++ii) {
26157 if (text[ii] === '\n') {
26163 this.parserState(text[ii]);
26169 Roo.rtf.Parser.prototype = {
26170 text : '', // string being parsed..
26172 controlWordParam : '',
26176 groupStack : false,
26181 row : 1, // reportin?
26185 push : function (el)
26187 var m = 'cmd'+ el.type;
26188 if (typeof(this[m]) == 'undefined') {
26189 Roo.log('invalid cmd:' + el.type);
26195 flushHexStore : function()
26197 if (this.hexStore.length < 1) {
26200 var hexstr = this.hexStore.map(
26205 this.group.addContent( new Roo.rtf.Hex( hexstr ));
26208 this.hexStore.splice(0)
26212 cmdgroupstart : function()
26214 this.flushHexStore();
26216 this.groupStack.push(this.group);
26219 if (this.doc === false) {
26220 this.group = this.doc = new Roo.rtf.Document();
26224 this.group = new Roo.rtf.Group(this.group);
26226 cmdignorable : function()
26228 this.flushHexStore();
26229 this.group.ignorable = true;
26231 cmdendparagraph : function()
26233 this.flushHexStore();
26234 this.group.addContent(new Roo.rtf.Paragraph());
26236 cmdgroupend : function ()
26238 this.flushHexStore();
26239 var endingGroup = this.group;
26242 this.group = this.groupStack.pop();
26244 this.group.addChild(endingGroup);
26249 var doc = this.group || this.doc;
26250 //if (endingGroup instanceof FontTable) {
26251 // doc.fonts = endingGroup.table
26252 //} else if (endingGroup instanceof ColorTable) {
26253 // doc.colors = endingGroup.table
26254 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26255 if (endingGroup.ignorable === false) {
26257 this.groups.push(endingGroup);
26258 // Roo.log( endingGroup );
26260 //Roo.each(endingGroup.content, function(item)) {
26261 // doc.addContent(item);
26263 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26266 cmdtext : function (cmd)
26268 this.flushHexStore();
26269 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26270 //this.group = this.doc
26271 return; // we really don't care about stray text...
26273 this.group.addContent(new Roo.rtf.Span(cmd));
26275 cmdcontrolword : function (cmd)
26277 this.flushHexStore();
26278 if (!this.group.type) {
26279 this.group.type = cmd.value;
26282 this.group.addContent(new Roo.rtf.Ctrl(cmd));
26283 // we actually don't care about ctrl words...
26286 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26287 if (this[method]) {
26288 this[method](cmd.param)
26290 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26294 cmdhexchar : function(cmd) {
26295 this.hexStore.push(cmd);
26297 cmderror : function(cmd) {
26303 if (this.text !== '\u0000') this.emitText()
26309 parseText : function(c)
26312 this.parserState = this.parseEscapes;
26313 } else if (c === '{') {
26314 this.emitStartGroup();
26315 } else if (c === '}') {
26316 this.emitEndGroup();
26317 } else if (c === '\x0A' || c === '\x0D') {
26318 // cr/lf are noise chars
26324 parseEscapes: function (c)
26326 if (c === '\\' || c === '{' || c === '}') {
26328 this.parserState = this.parseText;
26330 this.parserState = this.parseControlSymbol;
26331 this.parseControlSymbol(c);
26334 parseControlSymbol: function(c)
26337 this.text += '\u00a0'; // nbsp
26338 this.parserState = this.parseText
26339 } else if (c === '-') {
26340 this.text += '\u00ad'; // soft hyphen
26341 } else if (c === '_') {
26342 this.text += '\u2011'; // non-breaking hyphen
26343 } else if (c === '*') {
26344 this.emitIgnorable();
26345 this.parserState = this.parseText;
26346 } else if (c === "'") {
26347 this.parserState = this.parseHexChar;
26348 } else if (c === '|') { // formula cacter
26349 this.emitFormula();
26350 this.parserState = this.parseText;
26351 } else if (c === ':') { // subentry in an index entry
26352 this.emitIndexSubEntry();
26353 this.parserState = this.parseText;
26354 } else if (c === '\x0a') {
26355 this.emitEndParagraph();
26356 this.parserState = this.parseText;
26357 } else if (c === '\x0d') {
26358 this.emitEndParagraph();
26359 this.parserState = this.parseText;
26361 this.parserState = this.parseControlWord;
26362 this.parseControlWord(c);
26365 parseHexChar: function (c)
26367 if (/^[A-Fa-f0-9]$/.test(c)) {
26369 if (this.hexChar.length >= 2) {
26370 this.emitHexChar();
26371 this.parserState = this.parseText;
26375 this.emitError("Invalid character \"" + c + "\" in hex literal.");
26376 this.parserState = this.parseText;
26379 parseControlWord : function(c)
26382 this.emitControlWord();
26383 this.parserState = this.parseText;
26384 } else if (/^[-\d]$/.test(c)) {
26385 this.parserState = this.parseControlWordParam;
26386 this.controlWordParam += c;
26387 } else if (/^[A-Za-z]$/.test(c)) {
26388 this.controlWord += c;
26390 this.emitControlWord();
26391 this.parserState = this.parseText;
26395 parseControlWordParam : function (c) {
26396 if (/^\d$/.test(c)) {
26397 this.controlWordParam += c;
26398 } else if (c === ' ') {
26399 this.emitControlWord();
26400 this.parserState = this.parseText;
26402 this.emitControlWord();
26403 this.parserState = this.parseText;
26411 emitText : function () {
26412 if (this.text === '') {
26424 emitControlWord : function ()
26427 if (this.controlWord === '') {
26428 // do we want to track this - it seems just to cause problems.
26429 //this.emitError('empty control word');
26432 type: 'controlword',
26433 value: this.controlWord,
26434 param: this.controlWordParam !== '' && Number(this.controlWordParam),
26440 this.controlWord = '';
26441 this.controlWordParam = '';
26443 emitStartGroup : function ()
26447 type: 'groupstart',
26453 emitEndGroup : function ()
26463 emitIgnorable : function ()
26473 emitHexChar : function ()
26478 value: this.hexChar,
26485 emitError : function (message)
26493 char: this.cpos //,
26494 //stack: new Error().stack
26497 emitEndParagraph : function () {
26500 type: 'endparagraph',
26509 * @class Roo.htmleditor.Filter
26510 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26511 * @cfg {DomElement} node The node to iterate and filter
26512 * @cfg {boolean|String|Array} tag Tags to replace
26514 * Create a new Filter.
26515 * @param {Object} config Configuration options
26520 Roo.htmleditor.Filter = function(cfg) {
26521 Roo.apply(this.cfg);
26522 // this does not actually call walk as it's really just a abstract class
26526 Roo.htmleditor.Filter.prototype = {
26532 // overrride to do replace comments.
26533 replaceComment : false,
26535 // overrride to do replace or do stuff with tags..
26536 replaceTag : false,
26538 walk : function(dom)
26540 Roo.each( Array.from(dom.childNodes), function( e ) {
26543 case e.nodeType == 8 && this.replaceComment !== false: // comment
26544 this.replaceComment(e);
26547 case e.nodeType != 1: //not a node.
26550 case this.tag === true: // everything
26551 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26552 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26553 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26554 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26555 if (this.replaceTag && false === this.replaceTag(e)) {
26558 if (e.hasChildNodes()) {
26563 default: // tags .. that do not match.
26564 if (e.hasChildNodes()) {
26574 removeNodeKeepChildren : function( node)
26577 ar = Array.from(node.childNodes);
26578 for (var i = 0; i < ar.length; i++) {
26580 node.removeChild(ar[i]);
26581 // what if we need to walk these???
26582 node.parentNode.insertBefore(ar[i], node);
26585 node.parentNode.removeChild(node);
26590 * @class Roo.htmleditor.FilterAttributes
26591 * clean attributes and styles including http:// etc.. in attribute
26593 * Run a new Attribute Filter
26594 * @param {Object} config Configuration options
26596 Roo.htmleditor.FilterAttributes = function(cfg)
26598 Roo.apply(this, cfg);
26599 this.attrib_black = this.attrib_black || [];
26600 this.attrib_white = this.attrib_white || [];
26602 this.attrib_clean = this.attrib_clean || [];
26603 this.style_white = this.style_white || [];
26604 this.style_black = this.style_black || [];
26605 this.walk(cfg.node);
26608 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26610 tag: true, // all tags
26612 attrib_black : false, // array
26613 attrib_clean : false,
26614 attrib_white : false,
26616 style_white : false,
26617 style_black : false,
26620 replaceTag : function(node)
26622 if (!node.attributes || !node.attributes.length) {
26626 for (var i = node.attributes.length-1; i > -1 ; i--) {
26627 var a = node.attributes[i];
26629 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26630 node.removeAttribute(a.name);
26636 if (a.name.toLowerCase().substr(0,2)=='on') {
26637 node.removeAttribute(a.name);
26642 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26643 node.removeAttribute(a.name);
26646 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26647 this.cleanAttr(node,a.name,a.value); // fixme..
26650 if (a.name == 'style') {
26651 this.cleanStyle(node,a.name,a.value);
26654 /// clean up MS crap..
26655 // tecnically this should be a list of valid class'es..
26658 if (a.name == 'class') {
26659 if (a.value.match(/^Mso/)) {
26660 node.removeAttribute('class');
26663 if (a.value.match(/^body$/)) {
26664 node.removeAttribute('class');
26674 return true; // clean children
26677 cleanAttr: function(node, n,v)
26680 if (v.match(/^\./) || v.match(/^\//)) {
26683 if (v.match(/^(http|https):\/\//)
26684 || v.match(/^mailto:/)
26685 || v.match(/^ftp:/)
26686 || v.match(/^data:/)
26690 if (v.match(/^#/)) {
26693 if (v.match(/^\{/)) { // allow template editing.
26696 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26697 node.removeAttribute(n);
26700 cleanStyle : function(node, n,v)
26702 if (v.match(/expression/)) { //XSS?? should we even bother..
26703 node.removeAttribute(n);
26707 var parts = v.split(/;/);
26710 Roo.each(parts, function(p) {
26711 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26715 var l = p.split(':').shift().replace(/\s+/g,'');
26716 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26718 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26722 // only allow 'c whitelisted system attributes'
26723 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26731 if (clean.length) {
26732 node.setAttribute(n, clean.join(';'));
26734 node.removeAttribute(n);
26743 * @class Roo.htmleditor.FilterBlack
26744 * remove blacklisted elements.
26746 * Run a new Blacklisted Filter
26747 * @param {Object} config Configuration options
26750 Roo.htmleditor.FilterBlack = function(cfg)
26752 Roo.apply(this, cfg);
26753 this.walk(cfg.node);
26756 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26758 tag : true, // all elements.
26760 replaceTag : function(n)
26762 n.parentNode.removeChild(n);
26766 * @class Roo.htmleditor.FilterComment
26769 * Run a new Comments Filter
26770 * @param {Object} config Configuration options
26772 Roo.htmleditor.FilterComment = function(cfg)
26774 this.walk(cfg.node);
26777 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26780 replaceComment : function(n)
26782 n.parentNode.removeChild(n);
26785 * @class Roo.htmleditor.FilterKeepChildren
26786 * remove tags but keep children
26788 * Run a new Keep Children Filter
26789 * @param {Object} config Configuration options
26792 Roo.htmleditor.FilterKeepChildren = function(cfg)
26794 Roo.apply(this, cfg);
26795 if (this.tag === false) {
26796 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26799 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26800 this.cleanNamespace = true;
26803 this.walk(cfg.node);
26806 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26808 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26810 replaceTag : function(node)
26812 // walk children...
26813 //Roo.log(node.tagName);
26814 var ar = Array.from(node.childNodes);
26817 for (var i = 0; i < ar.length; i++) {
26819 if (e.nodeType == 1) {
26821 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26822 || // array and it matches
26823 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26825 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26827 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26829 this.replaceTag(ar[i]); // child is blacklisted as well...
26834 ar = Array.from(node.childNodes);
26835 for (var i = 0; i < ar.length; i++) {
26837 node.removeChild(ar[i]);
26838 // what if we need to walk these???
26839 node.parentNode.insertBefore(ar[i], node);
26840 if (this.tag !== false) {
26845 //Roo.log("REMOVE:" + node.tagName);
26846 node.parentNode.removeChild(node);
26847 return false; // don't walk children
26852 * @class Roo.htmleditor.FilterParagraph
26853 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26854 * like on 'push' to remove the <p> tags and replace them with line breaks.
26856 * Run a new Paragraph Filter
26857 * @param {Object} config Configuration options
26860 Roo.htmleditor.FilterParagraph = function(cfg)
26862 // no need to apply config.
26863 this.walk(cfg.node);
26866 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26873 replaceTag : function(node)
26876 if (node.childNodes.length == 1 &&
26877 node.childNodes[0].nodeType == 3 &&
26878 node.childNodes[0].textContent.trim().length < 1
26880 // remove and replace with '<BR>';
26881 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26882 return false; // no need to walk..
26884 var ar = Array.from(node.childNodes);
26885 for (var i = 0; i < ar.length; i++) {
26886 node.removeChild(ar[i]);
26887 // what if we need to walk these???
26888 node.parentNode.insertBefore(ar[i], node);
26890 // now what about this?
26894 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26895 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26896 node.parentNode.removeChild(node);
26903 * @class Roo.htmleditor.FilterSpan
26904 * filter span's with no attributes out..
26906 * Run a new Span Filter
26907 * @param {Object} config Configuration options
26910 Roo.htmleditor.FilterSpan = function(cfg)
26912 // no need to apply config.
26913 this.walk(cfg.node);
26916 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26922 replaceTag : function(node)
26924 if (node.attributes && node.attributes.length > 0) {
26925 return true; // walk if there are any.
26927 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26933 * @class Roo.htmleditor.FilterTableWidth
26934 try and remove table width data - as that frequently messes up other stuff.
26936 * was cleanTableWidths.
26938 * Quite often pasting from word etc.. results in tables with column and widths.
26939 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26942 * Run a new Table Filter
26943 * @param {Object} config Configuration options
26946 Roo.htmleditor.FilterTableWidth = function(cfg)
26948 // no need to apply config.
26949 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26950 this.walk(cfg.node);
26953 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26958 replaceTag: function(node) {
26962 if (node.hasAttribute('width')) {
26963 node.removeAttribute('width');
26967 if (node.hasAttribute("style")) {
26970 var styles = node.getAttribute("style").split(";");
26972 Roo.each(styles, function(s) {
26973 if (!s.match(/:/)) {
26976 var kv = s.split(":");
26977 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26980 // what ever is left... we allow.
26983 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26984 if (!nstyle.length) {
26985 node.removeAttribute('style');
26989 return true; // continue doing children..
26992 * @class Roo.htmleditor.FilterWord
26993 * try and clean up all the mess that Word generates.
26995 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26998 * Run a new Span Filter
26999 * @param {Object} config Configuration options
27002 Roo.htmleditor.FilterWord = function(cfg)
27004 // no need to apply config.
27005 this.replaceDocBullets(cfg.node);
27007 this.replaceAname(cfg.node);
27008 // this is disabled as the removal is done by other filters;
27009 // this.walk(cfg.node);
27010 this.replaceImageTable(cfg.node);
27014 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27020 * Clean up MS wordisms...
27022 replaceTag : function(node)
27025 // no idea what this does - span with text, replaceds with just text.
27027 node.nodeName == 'SPAN' &&
27028 !node.hasAttributes() &&
27029 node.childNodes.length == 1 &&
27030 node.firstChild.nodeName == "#text"
27032 var textNode = node.firstChild;
27033 node.removeChild(textNode);
27034 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27035 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27037 node.parentNode.insertBefore(textNode, node);
27038 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27039 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27042 node.parentNode.removeChild(node);
27043 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27048 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27049 node.parentNode.removeChild(node);
27050 return false; // dont do chidlren
27052 //Roo.log(node.tagName);
27053 // remove - but keep children..
27054 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27055 //Roo.log('-- removed');
27056 while (node.childNodes.length) {
27057 var cn = node.childNodes[0];
27058 node.removeChild(cn);
27059 node.parentNode.insertBefore(cn, node);
27060 // move node to parent - and clean it..
27061 if (cn.nodeType == 1) {
27062 this.replaceTag(cn);
27066 node.parentNode.removeChild(node);
27067 /// no need to iterate chidlren = it's got none..
27068 //this.iterateChildren(node, this.cleanWord);
27069 return false; // no need to iterate children.
27072 if (node.className.length) {
27074 var cn = node.className.split(/\W+/);
27076 Roo.each(cn, function(cls) {
27077 if (cls.match(/Mso[a-zA-Z]+/)) {
27082 node.className = cna.length ? cna.join(' ') : '';
27084 node.removeAttribute("class");
27088 if (node.hasAttribute("lang")) {
27089 node.removeAttribute("lang");
27092 if (node.hasAttribute("style")) {
27094 var styles = node.getAttribute("style").split(";");
27096 Roo.each(styles, function(s) {
27097 if (!s.match(/:/)) {
27100 var kv = s.split(":");
27101 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27104 // what ever is left... we allow.
27107 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27108 if (!nstyle.length) {
27109 node.removeAttribute('style');
27112 return true; // do children
27118 styleToObject: function(node)
27120 var styles = (node.getAttribute("style") || '').split(";");
27122 Roo.each(styles, function(s) {
27123 if (!s.match(/:/)) {
27126 var kv = s.split(":");
27128 // what ever is left... we allow.
27129 ret[kv[0].trim()] = kv[1];
27135 replaceAname : function (doc)
27137 // replace all the a/name without..
27138 var aa = Array.from(doc.getElementsByTagName('a'));
27139 for (var i = 0; i < aa.length; i++) {
27141 if (a.hasAttribute("name")) {
27142 a.removeAttribute("name");
27144 if (a.hasAttribute("href")) {
27147 // reparent children.
27148 this.removeNodeKeepChildren(a);
27158 replaceDocBullets : function(doc)
27160 // this is a bit odd - but it appears some indents use ql-indent-1
27161 //Roo.log(doc.innerHTML);
27163 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27164 for( var i = 0; i < listpara.length; i ++) {
27165 listpara[i].className = "MsoListParagraph";
27168 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27169 for( var i = 0; i < listpara.length; i ++) {
27170 listpara[i].className = "MsoListParagraph";
27172 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27173 for( var i = 0; i < listpara.length; i ++) {
27174 listpara[i].className = "MsoListParagraph";
27176 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
27177 for( var i = 0; i < listpara.length; i ++) {
27178 listpara[i].className = "MsoListParagraph";
27181 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27182 var htwo = Array.from(doc.getElementsByTagName('h2'));
27183 for( var i = 0; i < htwo.length; i ++) {
27184 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27185 htwo[i].className = "MsoListParagraph";
27188 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
27189 for( var i = 0; i < listpara.length; i ++) {
27190 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27191 listpara[i].className = "MsoListParagraph";
27193 listpara[i].className = "MsoNormalx";
27197 listpara = doc.getElementsByClassName('MsoListParagraph');
27198 // Roo.log(doc.innerHTML);
27202 while(listpara.length) {
27204 this.replaceDocBullet(listpara.item(0));
27211 replaceDocBullet : function(p)
27213 // gather all the siblings.
27215 parent = p.parentNode,
27216 doc = parent.ownerDocument,
27219 //Roo.log("Parsing: " + p.innerText) ;
27220 var listtype = 'ul';
27222 if (ns.nodeType != 1) {
27223 ns = ns.nextSibling;
27226 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27227 //Roo.log("Missing para r q1indent - got:" + ns.className);
27230 var spans = ns.getElementsByTagName('span');
27232 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27234 ns = ns.nextSibling;
27236 if (!spans.length) {
27241 for (var i = 0; i < spans.length;i++) {
27243 if (se.hasAttribute('style') && se.hasAttribute('style') && se.style.fontFamily != '') {
27244 ff = se.style.fontFamily;
27250 //Roo.log("got font family: " + ff);
27251 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27257 //Roo.log("no mso-list?");
27259 var spans = ns.getElementsByTagName('span');
27260 if (!spans.length) {
27263 var has_list = false;
27264 for(var i = 0; i < spans.length; i++) {
27265 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27274 ns = ns.nextSibling;
27278 if (!items.length) {
27283 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27284 parent.insertBefore(ul, p);
27286 var stack = [ ul ];
27287 var last_li = false;
27289 var margin_to_depth = {};
27292 items.forEach(function(n, ipos) {
27293 //Roo.log("got innertHMLT=" + n.innerHTML);
27295 var spans = n.getElementsByTagName('span');
27296 if (!spans.length) {
27297 //Roo.log("No spans found");
27299 parent.removeChild(n);
27302 return; // skip it...
27308 for(var i = 0; i < spans.length; i++) {
27310 style = this.styleToObject(spans[i]);
27311 if (typeof(style['mso-list']) == 'undefined') {
27314 if (listtype == 'ol') {
27315 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
27317 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27320 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27321 style = this.styleToObject(n); // mo-list is from the parent node.
27322 if (typeof(style['mso-list']) == 'undefined') {
27323 //Roo.log("parent is missing level");
27325 parent.removeChild(n);
27330 var margin = style['margin-left'];
27331 if (typeof(margin_to_depth[margin]) == 'undefined') {
27333 margin_to_depth[margin] = max_margins;
27335 nlvl = margin_to_depth[margin] ;
27339 var nul = doc.createElement(listtype); // what about number lists...
27341 last_li = doc.createElement('li');
27342 stack[lvl].appendChild(last_li);
27344 last_li.appendChild(nul);
27350 // not starting at 1..
27351 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27352 stack[nlvl].setAttribute("start", num);
27355 var nli = stack[nlvl].appendChild(doc.createElement('li'));
27357 nli.innerHTML = n.innerHTML;
27358 //Roo.log("innerHTML = " + n.innerHTML);
27359 parent.removeChild(n);
27371 replaceImageTable : function(doc)
27374 <table cellpadding=0 cellspacing=0 align=left>
27376 <td width=423 height=0></td>
27380 <td><img width=601 height=401
27381 src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27382 v:shapes="Picture_x0020_2"></td>
27386 var imgs = Array.from(doc.getElementsByTagName('img'));
27387 Roo.each(imgs, function(img) {
27388 var td = img.parentNode;
27389 if (td.nodeName != 'TD') {
27392 var tr = td.parentNode;
27393 if (tr.nodeName != 'TR') {
27396 var tbody = tr.parentNode;
27397 if (tbody.nodeName != 'TBODY') {
27400 var table = tbody.parentNode;
27401 if (table.nodeName != 'TABLE') {
27406 if (table.getElementsByTagName('tr').length != 2) {
27409 if (table.getElementsByTagName('td').length != 3) {
27412 if (table.innerText.trim() != '') {
27415 var p = table.parentNode;
27416 img.parentNode.removeChild(img);
27417 p.insertBefore(img, table);
27418 p.removeChild(table);
27429 * @class Roo.htmleditor.FilterStyleToTag
27430 * part of the word stuff... - certain 'styles' should be converted to tags.
27432 * font-weight: bold -> bold
27433 * ?? super / subscrit etc..
27436 * Run a new style to tag filter.
27437 * @param {Object} config Configuration options
27439 Roo.htmleditor.FilterStyleToTag = function(cfg)
27443 B : [ 'fontWeight' , 'bold'],
27444 I : [ 'fontStyle' , 'italic'],
27445 //pre : [ 'font-style' , 'italic'],
27446 // h1.. h6 ?? font-size?
27447 SUP : [ 'verticalAlign' , 'super' ],
27448 SUB : [ 'verticalAlign' , 'sub' ]
27453 Roo.apply(this, cfg);
27456 this.walk(cfg.node);
27463 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27465 tag: true, // all tags
27470 replaceTag : function(node)
27474 if (node.getAttribute("style") === null) {
27478 for (var k in this.tags) {
27479 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27481 node.style.removeProperty(this.tags[k][0]);
27484 if (!inject.length) {
27487 var cn = Array.from(node.childNodes);
27489 Roo.each(inject, function(t) {
27490 var nc = node.ownerDocument.createElement(t);
27491 nn.appendChild(nc);
27494 for(var i = 0;i < cn.length;cn++) {
27495 node.removeChild(cn[i]);
27496 nn.appendChild(cn[i]);
27498 return true /// iterate thru
27502 * @class Roo.htmleditor.FilterLongBr
27503 * BR/BR/BR - keep a maximum of 2...
27505 * Run a new Long BR Filter
27506 * @param {Object} config Configuration options
27509 Roo.htmleditor.FilterLongBr = function(cfg)
27511 // no need to apply config.
27512 this.walk(cfg.node);
27515 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27522 replaceTag : function(node)
27525 var ps = node.nextSibling;
27526 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27527 ps = ps.nextSibling;
27530 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
27531 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27535 if (!ps || ps.nodeType != 1) {
27539 if (!ps || ps.tagName != 'BR') {
27548 if (!node.previousSibling) {
27551 var ps = node.previousSibling;
27553 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27554 ps = ps.previousSibling;
27556 if (!ps || ps.nodeType != 1) {
27559 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27560 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27564 node.parentNode.removeChild(node); // remove me...
27566 return false; // no need to do children
27573 * @class Roo.htmleditor.FilterBlock
27574 * removes id / data-block and contenteditable that are associated with blocks
27575 * usage should be done on a cloned copy of the dom
27577 * Run a new Attribute Filter { node : xxxx }}
27578 * @param {Object} config Configuration options
27580 Roo.htmleditor.FilterBlock = function(cfg)
27582 Roo.apply(this, cfg);
27583 var qa = cfg.node.querySelectorAll;
27584 this.removeAttributes('data-block');
27585 this.removeAttributes('contenteditable');
27586 this.removeAttributes('id');
27590 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27592 node: true, // all tags
27595 removeAttributes : function(attr)
27597 var ar = this.node.querySelectorAll('*[' + attr + ']');
27598 for (var i =0;i<ar.length;i++) {
27599 ar[i].removeAttribute(attr);
27608 * This is based loosely on tinymce
27609 * @class Roo.htmleditor.TidySerializer
27610 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27612 * @method Serializer
27613 * @param {Object} settings Name/value settings object.
27617 Roo.htmleditor.TidySerializer = function(settings)
27619 Roo.apply(this, settings);
27621 this.writer = new Roo.htmleditor.TidyWriter(settings);
27626 Roo.htmleditor.TidySerializer.prototype = {
27629 * @param {boolean} inner do the inner of the node.
27636 * Serializes the specified node into a string.
27639 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27640 * @method serialize
27641 * @param {DomElement} node Node instance to serialize.
27642 * @return {String} String with HTML based on DOM tree.
27644 serialize : function(node) {
27646 // = settings.validate;
27647 var writer = this.writer;
27651 3: function(node) {
27653 writer.text(node.nodeValue, node);
27656 8: function(node) {
27657 writer.comment(node.nodeValue);
27659 // Processing instruction
27660 7: function(node) {
27661 writer.pi(node.name, node.nodeValue);
27664 10: function(node) {
27665 writer.doctype(node.nodeValue);
27668 4: function(node) {
27669 writer.cdata(node.nodeValue);
27671 // Document fragment
27672 11: function(node) {
27673 node = node.firstChild;
27679 node = node.nextSibling
27684 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27685 return writer.getContent();
27688 walk: function(node)
27690 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27691 handler = this.handlers[node.nodeType];
27698 var name = node.nodeName;
27699 var isEmpty = node.childNodes.length < 1;
27701 var writer = this.writer;
27702 var attrs = node.attributes;
27705 writer.start(node.nodeName, attrs, isEmpty, node);
27709 node = node.firstChild;
27716 node = node.nextSibling;
27722 // Serialize element and treat all non elements as fragments
27727 * This is based loosely on tinymce
27728 * @class Roo.htmleditor.TidyWriter
27729 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27732 * - not tested much with 'PRE' formated elements.
27738 Roo.htmleditor.TidyWriter = function(settings)
27741 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27742 Roo.apply(this, settings);
27746 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27749 Roo.htmleditor.TidyWriter.prototype = {
27756 // part of state...
27760 last_inline : false,
27765 * Writes the a start element such as <p id="a">.
27768 * @param {String} name Name of the element.
27769 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27770 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27772 start: function(name, attrs, empty, node)
27774 var i, l, attr, value;
27776 // there are some situations where adding line break && indentation will not work. will not work.
27777 // <span / b / i ... formating?
27779 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27780 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27782 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27784 var add_lb = name == 'BR' ? false : in_inline;
27786 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27790 var indentstr = this.indentstr;
27792 // e_inline = elements that can be inline, but still allow \n before and after?
27793 // only 'BR' ??? any others?
27795 // ADD LINE BEFORE tage
27796 if (!this.in_pre) {
27799 if (name == 'BR') {
27801 } else if (this.lastElementEndsWS()) {
27804 // otherwise - no new line. (and dont indent.)
27815 this.html.push(indentstr + '<', name.toLowerCase());
27818 for (i = 0, l = attrs.length; i < l; i++) {
27820 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27826 this.html[this.html.length] = '/>';
27828 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27830 var e_inline = name == 'BR' ? false : this.in_inline;
27832 if (!e_inline && !this.in_pre) {
27839 this.html[this.html.length] = '>';
27841 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27843 if (!in_inline && !in_pre) {
27844 var cn = node.firstChild;
27846 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27850 cn = cn.nextSibling;
27858 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27860 in_inline : in_inline
27862 // add a line after if we are not in a
27864 if (!in_inline && !in_pre) {
27873 lastElementEndsWS : function()
27875 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27876 if (value === false) {
27879 return value.match(/\s+$/);
27884 * Writes the a end element such as </p>.
27887 * @param {String} name Name of the element.
27889 end: function(name) {
27892 var indentstr = '';
27893 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27895 if (!this.in_pre && !in_inline) {
27897 indentstr = this.indentstr;
27899 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27900 this.last_inline = in_inline;
27902 // pop the indent state..
27905 * Writes a text node.
27907 * In pre - we should not mess with the contents.
27911 * @param {String} text String to write out.
27912 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27914 text: function(in_text, node)
27916 // if not in whitespace critical
27917 if (in_text.length < 1) {
27920 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27923 this.html[this.html.length] = text;
27927 if (this.in_inline) {
27928 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27930 text = text.replace(/\s+/,' '); // all white space to single white space
27933 // if next tag is '<BR>', then we can trim right..
27934 if (node.nextSibling &&
27935 node.nextSibling.nodeType == 1 &&
27936 node.nextSibling.nodeName == 'BR' )
27938 text = text.replace(/\s+$/g,'');
27940 // if previous tag was a BR, we can also trim..
27941 if (node.previousSibling &&
27942 node.previousSibling.nodeType == 1 &&
27943 node.previousSibling.nodeName == 'BR' )
27945 text = this.indentstr + text.replace(/^\s+/g,'');
27947 if (text.match(/\n/)) {
27948 text = text.replace(
27949 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27951 // remoeve the last whitespace / line break.
27952 text = text.replace(/\n\s+$/,'');
27954 // repace long lines
27958 this.html[this.html.length] = text;
27961 // see if previous element was a inline element.
27962 var indentstr = this.indentstr;
27964 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27966 // should trim left?
27967 if (node.previousSibling &&
27968 node.previousSibling.nodeType == 1 &&
27969 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27975 text = text.replace(/^\s+/,''); // trim left
27978 // should trim right?
27979 if (node.nextSibling &&
27980 node.nextSibling.nodeType == 1 &&
27981 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27986 text = text.replace(/\s+$/,''); // trim right
27993 if (text.length < 1) {
27996 if (!text.match(/\n/)) {
27997 this.html.push(indentstr + text);
28001 text = this.indentstr + text.replace(
28002 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28004 // remoeve the last whitespace / line break.
28005 text = text.replace(/\s+$/,'');
28007 this.html.push(text);
28009 // split and indent..
28014 * Writes a cdata node such as <![CDATA[data]]>.
28017 * @param {String} text String to write out inside the cdata.
28019 cdata: function(text) {
28020 this.html.push('<![CDATA[', text, ']]>');
28023 * Writes a comment node such as <!-- Comment -->.
28026 * @param {String} text String to write out inside the comment.
28028 comment: function(text) {
28029 this.html.push('<!--', text, '-->');
28032 * Writes a PI node such as <?xml attr="value" ?>.
28035 * @param {String} name Name of the pi.
28036 * @param {String} text String to write out inside the pi.
28038 pi: function(name, text) {
28039 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28040 this.indent != '' && this.html.push('\n');
28043 * Writes a doctype node such as <!DOCTYPE data>.
28046 * @param {String} text String to write out inside the doctype.
28048 doctype: function(text) {
28049 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28052 * Resets the internal buffer if one wants to reuse the writer.
28056 reset: function() {
28057 this.html.length = 0;
28066 * Returns the contents that got serialized.
28068 * @method getContent
28069 * @return {String} HTML contents that got written down.
28071 getContent: function() {
28072 return this.html.join('').replace(/\n$/, '');
28075 pushState : function(cfg)
28077 this.state.push(cfg);
28078 Roo.apply(this, cfg);
28081 popState : function()
28083 if (this.state.length < 1) {
28084 return; // nothing to push
28091 if (this.state.length > 0) {
28092 cfg = this.state[this.state.length-1];
28094 Roo.apply(this, cfg);
28097 addLine: function()
28099 if (this.html.length < 1) {
28104 var value = this.html[this.html.length - 1];
28105 if (value.length > 0 && '\n' !== value) {
28106 this.html.push('\n');
28111 //'pre script noscript style textarea video audio iframe object code'
28112 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
28116 Roo.htmleditor.TidyWriter.inline_elements = [
28117 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28118 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28120 Roo.htmleditor.TidyWriter.shortend_elements = [
28121 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28122 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28125 Roo.htmleditor.TidyWriter.whitespace_elements = [
28126 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28128 * This is based loosely on tinymce
28129 * @class Roo.htmleditor.TidyEntities
28131 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28133 * Not 100% sure this is actually used or needed.
28136 Roo.htmleditor.TidyEntities = {
28139 * initialize data..
28141 init : function (){
28143 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28148 buildEntitiesLookup: function(items, radix) {
28149 var i, chr, entity, lookup = {};
28153 items = typeof(items) == 'string' ? items.split(',') : items;
28154 radix = radix || 10;
28155 // Build entities lookup table
28156 for (i = 0; i < items.length; i += 2) {
28157 chr = String.fromCharCode(parseInt(items[i], radix));
28158 // Only add non base entities
28159 if (!this.baseEntities[chr]) {
28160 entity = '&' + items[i + 1] + ';';
28161 lookup[chr] = entity;
28162 lookup[entity] = chr;
28201 // Needs to be escaped since the YUI compressor would otherwise break the code
28208 // Reverse lookup table for raw entities
28209 reverseEntities : {
28217 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28218 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28219 rawCharsRegExp : /[<>&\"\']/g,
28220 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28221 namedEntities : false,
28222 namedEntitiesData : [
28723 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28725 * @method encodeRaw
28726 * @param {String} text Text to encode.
28727 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28728 * @return {String} Entity encoded text.
28730 encodeRaw: function(text, attr)
28733 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28734 return t.baseEntities[chr] || chr;
28738 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28739 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28740 * and is exposed as the DOMUtils.encode function.
28742 * @method encodeAllRaw
28743 * @param {String} text Text to encode.
28744 * @return {String} Entity encoded text.
28746 encodeAllRaw: function(text) {
28748 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28749 return t.baseEntities[chr] || chr;
28753 * Encodes the specified string using numeric entities. The core entities will be
28754 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28756 * @method encodeNumeric
28757 * @param {String} text Text to encode.
28758 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28759 * @return {String} Entity encoded text.
28761 encodeNumeric: function(text, attr) {
28763 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28764 // Multi byte sequence convert it to a single entity
28765 if (chr.length > 1) {
28766 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28768 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28772 * Encodes the specified string using named entities. The core entities will be encoded
28773 * as named ones but all non lower ascii characters will be encoded into named entities.
28775 * @method encodeNamed
28776 * @param {String} text Text to encode.
28777 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28778 * @param {Object} entities Optional parameter with entities to use.
28779 * @return {String} Entity encoded text.
28781 encodeNamed: function(text, attr, entities) {
28783 entities = entities || this.namedEntities;
28784 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28785 return t.baseEntities[chr] || entities[chr] || chr;
28789 * Returns an encode function based on the name(s) and it's optional entities.
28791 * @method getEncodeFunc
28792 * @param {String} name Comma separated list of encoders for example named,numeric.
28793 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28794 * @return {function} Encode function to be used.
28796 getEncodeFunc: function(name, entities) {
28797 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28799 function encodeNamedAndNumeric(text, attr) {
28800 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28801 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28805 function encodeCustomNamed(text, attr) {
28806 return t.encodeNamed(text, attr, entities);
28808 // Replace + with , to be compatible with previous TinyMCE versions
28809 name = this.makeMap(name.replace(/\+/g, ','));
28810 // Named and numeric encoder
28811 if (name.named && name.numeric) {
28812 return this.encodeNamedAndNumeric;
28818 return encodeCustomNamed;
28820 return this.encodeNamed;
28823 if (name.numeric) {
28824 return this.encodeNumeric;
28827 return this.encodeRaw;
28830 * Decodes the specified string, this will replace entities with raw UTF characters.
28833 * @param {String} text Text to entity decode.
28834 * @return {String} Entity decoded string.
28836 decode: function(text)
28839 return text.replace(this.entityRegExp, function(all, numeric) {
28841 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28842 // Support upper UTF
28843 if (numeric > 65535) {
28845 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28847 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28849 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28852 nativeDecode : function (text) {
28855 makeMap : function (items, delim, map) {
28857 items = items || [];
28858 delim = delim || ',';
28859 if (typeof items == "string") {
28860 items = items.split(delim);
28865 map[items[i]] = {};
28873 Roo.htmleditor.TidyEntities.init();
28875 * @class Roo.htmleditor.KeyEnter
28876 * Handle Enter press..
28877 * @cfg {Roo.HtmlEditorCore} core the editor.
28879 * Create a new Filter.
28880 * @param {Object} config Configuration options
28887 Roo.htmleditor.KeyEnter = function(cfg) {
28888 Roo.apply(this, cfg);
28889 // this does not actually call walk as it's really just a abstract class
28891 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28894 //Roo.htmleditor.KeyEnter.i = 0;
28897 Roo.htmleditor.KeyEnter.prototype = {
28901 keypress : function(e)
28903 if (e.charCode != 13 && e.charCode != 10) {
28904 Roo.log([e.charCode,e]);
28907 e.preventDefault();
28908 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28909 var doc = this.core.doc;
28913 var sel = this.core.getSelection();
28914 var range = sel.getRangeAt(0);
28915 var n = range.commonAncestorContainer;
28916 var pc = range.closest([ 'ol', 'ul']);
28917 var pli = range.closest('li');
28918 if (!pc || e.ctrlKey) {
28919 // on it list, or ctrl pressed.
28921 sel.insertNode('br', 'after');
28923 // only do this if we have ctrl key..
28924 var br = doc.createElement('br');
28925 br.className = 'clear';
28926 br.setAttribute('style', 'clear: both');
28927 sel.insertNode(br, 'after');
28931 this.core.undoManager.addEvent();
28932 this.core.fireEditorEvent(e);
28936 // deal with <li> insetion
28937 if (pli.innerText.trim() == '' &&
28938 pli.previousSibling &&
28939 pli.previousSibling.nodeName == 'LI' &&
28940 pli.previousSibling.innerText.trim() == '') {
28941 pli.parentNode.removeChild(pli.previousSibling);
28942 sel.cursorAfter(pc);
28943 this.core.undoManager.addEvent();
28944 this.core.fireEditorEvent(e);
28948 var li = doc.createElement('LI');
28949 li.innerHTML = ' ';
28950 if (!pli || !pli.firstSibling) {
28951 pc.appendChild(li);
28953 pli.parentNode.insertBefore(li, pli.firstSibling);
28955 sel.cursorText (li.firstChild);
28957 this.core.undoManager.addEvent();
28958 this.core.fireEditorEvent(e);
28970 * @class Roo.htmleditor.Block
28971 * Base class for html editor blocks - do not use it directly .. extend it..
28972 * @cfg {DomElement} node The node to apply stuff to.
28973 * @cfg {String} friendly_name the name that appears in the context bar about this block
28974 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28977 * Create a new Filter.
28978 * @param {Object} config Configuration options
28981 Roo.htmleditor.Block = function(cfg)
28983 // do nothing .. should not be called really.
28986 * factory method to get the block from an element (using cache if necessary)
28988 * @param {HtmlElement} the dom element
28990 Roo.htmleditor.Block.factory = function(node)
28992 var cc = Roo.htmleditor.Block.cache;
28993 var id = Roo.get(node).id;
28994 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28995 Roo.htmleditor.Block.cache[id].readElement(node);
28996 return Roo.htmleditor.Block.cache[id];
28998 var db = node.getAttribute('data-block');
29000 db = node.nodeName.toLowerCase().toUpperCaseFirst();
29002 var cls = Roo.htmleditor['Block' + db];
29003 if (typeof(cls) == 'undefined') {
29004 //Roo.log(node.getAttribute('data-block'));
29005 Roo.log("OOps missing block : " + 'Block' + db);
29008 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29009 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
29013 * initalize all Elements from content that are 'blockable'
29015 * @param the body element
29017 Roo.htmleditor.Block.initAll = function(body, type)
29019 if (typeof(type) == 'undefined') {
29020 var ia = Roo.htmleditor.Block.initAll;
29026 Roo.each(Roo.get(body).query(type), function(e) {
29027 Roo.htmleditor.Block.factory(e);
29030 // question goes here... do we need to clear out this cache sometimes?
29031 // or show we make it relivant to the htmleditor.
29032 Roo.htmleditor.Block.cache = {};
29034 Roo.htmleditor.Block.prototype = {
29038 // used by context menu
29039 friendly_name : 'Based Block',
29041 // text for button to delete this element
29042 deleteTitle : false,
29046 * Update a node with values from this object
29047 * @param {DomElement} node
29049 updateElement : function(node)
29051 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29054 * convert to plain HTML for calling insertAtCursor..
29056 toHTML : function()
29058 return Roo.DomHelper.markup(this.toObject());
29061 * used by readEleemnt to extract data from a node
29062 * may need improving as it's pretty basic
29064 * @param {DomElement} node
29065 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29066 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29067 * @param {String} style the style property - eg. text-align
29069 getVal : function(node, tag, attr, style)
29072 if (tag !== true && n.tagName != tag.toUpperCase()) {
29073 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29074 // but kiss for now.
29075 n = node.getElementsByTagName(tag).item(0);
29080 if (attr === false) {
29083 if (attr == 'html') {
29084 return n.innerHTML;
29086 if (attr == 'style') {
29087 return n.style[style];
29090 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29094 * create a DomHelper friendly object - for use with
29095 * Roo.DomHelper.markup / overwrite / etc..
29098 toObject : function()
29103 * Read a node that has a 'data-block' property - and extract the values from it.
29104 * @param {DomElement} node - the node
29106 readElement : function(node)
29117 * @class Roo.htmleditor.BlockFigure
29118 * Block that has an image and a figcaption
29119 * @cfg {String} image_src the url for the image
29120 * @cfg {String} align (left|right) alignment for the block default left
29121 * @cfg {String} caption the text to appear below (and in the alt tag)
29122 * @cfg {String} caption_display (block|none) display or not the caption
29123 * @cfg {String|number} image_width the width of the image number or %?
29124 * @cfg {String|number} image_height the height of the image number or %?
29127 * Create a new Filter.
29128 * @param {Object} config Configuration options
29131 Roo.htmleditor.BlockFigure = function(cfg)
29134 this.readElement(cfg.node);
29135 this.updateElement(cfg.node);
29137 Roo.apply(this, cfg);
29139 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29146 caption_display : 'block',
29152 // margin: '2%', not used
29154 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
29157 // used by context menu
29158 friendly_name : 'Image with caption',
29159 deleteTitle : "Delete Image and Caption",
29161 contextMenu : function(toolbar)
29164 var block = function() {
29165 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29169 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29171 var syncValue = toolbar.editorcore.syncValue;
29177 xtype : 'TextItem',
29179 xns : rooui.Toolbar //Boostrap?
29183 text: 'Change Image URL',
29186 click: function (btn, state)
29190 Roo.MessageBox.show({
29191 title : "Image Source URL",
29192 msg : "Enter the url for the image",
29193 buttons: Roo.MessageBox.OKCANCEL,
29194 fn: function(btn, val){
29201 toolbar.editorcore.onEditorEvent();
29205 //multiline: multiline,
29207 value : b.image_src
29211 xns : rooui.Toolbar
29216 text: 'Change Link URL',
29219 click: function (btn, state)
29223 Roo.MessageBox.show({
29224 title : "Link URL",
29225 msg : "Enter the url for the link - leave blank to have no link",
29226 buttons: Roo.MessageBox.OKCANCEL,
29227 fn: function(btn, val){
29234 toolbar.editorcore.onEditorEvent();
29238 //multiline: multiline,
29244 xns : rooui.Toolbar
29248 text: 'Show Video URL',
29251 click: function (btn, state)
29253 Roo.MessageBox.alert("Video URL",
29254 block().video_url == '' ? 'This image is not linked ot a video' :
29255 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29258 xns : rooui.Toolbar
29263 xtype : 'TextItem',
29265 xns : rooui.Toolbar //Boostrap?
29268 xtype : 'ComboBox',
29269 allowBlank : false,
29270 displayField : 'val',
29273 triggerAction : 'all',
29275 valueField : 'val',
29279 select : function (combo, r, index)
29281 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29283 b.width = r.get('val');
29286 toolbar.editorcore.onEditorEvent();
29291 xtype : 'SimpleStore',
29304 xtype : 'TextItem',
29306 xns : rooui.Toolbar //Boostrap?
29309 xtype : 'ComboBox',
29310 allowBlank : false,
29311 displayField : 'val',
29314 triggerAction : 'all',
29316 valueField : 'val',
29320 select : function (combo, r, index)
29322 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29324 b.align = r.get('val');
29327 toolbar.editorcore.onEditorEvent();
29332 xtype : 'SimpleStore',
29346 text: 'Hide Caption',
29347 name : 'caption_display',
29349 enableToggle : true,
29350 setValue : function(v) {
29351 // this trigger toggle.
29353 this.setText(v ? "Hide Caption" : "Show Caption");
29354 this.setPressed(v != 'block');
29357 toggle: function (btn, state)
29360 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29361 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29364 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29365 toolbar.editorcore.onEditorEvent();
29368 xns : rooui.Toolbar
29374 * create a DomHelper friendly object - for use with
29375 * Roo.DomHelper.markup / overwrite / etc..
29377 toObject : function()
29379 var d = document.createElement('div');
29380 d.innerHTML = this.caption;
29382 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
29384 var iw = this.align == 'center' ? this.width : '100%';
29387 contenteditable : 'false',
29388 src : this.image_src,
29389 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29392 maxWidth : iw + ' !important', // this is not getting rendered?
29398 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29400 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
29405 if (this.href.length > 0) {
29409 contenteditable : 'true',
29417 if (this.video_url.length > 0) {
29422 allowfullscreen : true,
29423 width : 420, // these are for video tricks - that we replace the outer
29425 src : this.video_url,
29431 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29432 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29437 'data-block' : 'Figure',
29438 'data-width' : this.width,
29439 'data-caption' : this.caption,
29440 contenteditable : 'false',
29444 float : this.align ,
29445 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29446 width : this.align == 'center' ? '100%' : this.width,
29448 padding: this.align == 'center' ? '0' : '0 10px' ,
29449 textAlign : this.align // seems to work for email..
29454 align : this.align,
29460 'data-display' : this.caption_display,
29462 textAlign : 'left',
29464 lineHeight : '24px',
29465 display : this.caption_display,
29466 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
29468 width: this.align == 'center' ? this.width : '100%'
29472 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
29477 marginTop : '16px',
29483 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
29485 contenteditable : true,
29501 readElement : function(node)
29503 // this should not really come from the link...
29504 this.video_url = this.getVal(node, 'div', 'src');
29505 this.cls = this.getVal(node, 'div', 'class');
29506 this.href = this.getVal(node, 'a', 'href');
29509 this.image_src = this.getVal(node, 'img', 'src');
29511 this.align = this.getVal(node, 'figure', 'align');
29513 /// not really used - as hidden captions do not store the content here..
29514 var figcaption = this.getVal(node, 'figcaption', false);
29515 if (figcaption !== '') {
29516 this.caption = this.getVal(figcaption, 'i', 'html');
29520 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29521 var dc = this.getVal(node, true, 'data-caption');
29522 if (dc && dc.length) {
29525 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29526 this.width = this.getVal(node, true, 'data-width');
29527 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29530 removeNode : function()
29547 * @class Roo.htmleditor.BlockTable
29548 * Block that manages a table
29551 * Create a new Filter.
29552 * @param {Object} config Configuration options
29555 Roo.htmleditor.BlockTable = function(cfg)
29558 this.readElement(cfg.node);
29559 this.updateElement(cfg.node);
29561 Roo.apply(this, cfg);
29564 for(var r = 0; r < this.no_row; r++) {
29566 for(var c = 0; c < this.no_col; c++) {
29567 this.rows[r][c] = this.emptyCell();
29574 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29583 // used by context menu
29584 friendly_name : 'Table',
29585 deleteTitle : 'Delete Table',
29586 // context menu is drawn once..
29588 contextMenu : function(toolbar)
29591 var block = function() {
29592 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29596 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29598 var syncValue = toolbar.editorcore.syncValue;
29604 xtype : 'TextItem',
29606 xns : rooui.Toolbar //Boostrap?
29609 xtype : 'ComboBox',
29610 allowBlank : false,
29611 displayField : 'val',
29614 triggerAction : 'all',
29616 valueField : 'val',
29620 select : function (combo, r, index)
29622 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29624 b.width = r.get('val');
29627 toolbar.editorcore.onEditorEvent();
29632 xtype : 'SimpleStore',
29644 xtype : 'TextItem',
29645 text : "Columns: ",
29646 xns : rooui.Toolbar //Boostrap?
29653 click : function (_self, e)
29655 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29656 block().removeColumn();
29658 toolbar.editorcore.onEditorEvent();
29661 xns : rooui.Toolbar
29667 click : function (_self, e)
29669 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29670 block().addColumn();
29672 toolbar.editorcore.onEditorEvent();
29675 xns : rooui.Toolbar
29679 xtype : 'TextItem',
29681 xns : rooui.Toolbar //Boostrap?
29688 click : function (_self, e)
29690 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29691 block().removeRow();
29693 toolbar.editorcore.onEditorEvent();
29696 xns : rooui.Toolbar
29702 click : function (_self, e)
29706 toolbar.editorcore.onEditorEvent();
29709 xns : rooui.Toolbar
29714 text: 'Reset Column Widths',
29717 click : function (_self, e)
29719 block().resetWidths();
29721 toolbar.editorcore.onEditorEvent();
29724 xns : rooui.Toolbar
29735 * create a DomHelper friendly object - for use with
29736 * Roo.DomHelper.markup / overwrite / etc..
29737 * ?? should it be called with option to hide all editing features?
29739 toObject : function()
29744 contenteditable : 'false', // this stops cell selection from picking the table.
29745 'data-block' : 'Table',
29748 border : 'solid 1px #000', // ??? hard coded?
29749 'border-collapse' : 'collapse'
29752 { tag : 'tbody' , cn : [] }
29756 // do we have a head = not really
29758 Roo.each(this.rows, function( row ) {
29763 border : 'solid 1px #000',
29769 ret.cn[0].cn.push(tr);
29770 // does the row have any properties? ?? height?
29772 Roo.each(row, function( cell ) {
29776 contenteditable : 'true',
29777 'data-block' : 'Td',
29781 if (cell.colspan > 1) {
29782 td.colspan = cell.colspan ;
29783 nc += cell.colspan;
29787 if (cell.rowspan > 1) {
29788 td.rowspan = cell.rowspan ;
29797 ncols = Math.max(nc, ncols);
29801 // add the header row..
29810 readElement : function(node)
29812 node = node ? node : this.node ;
29813 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29817 var trs = Array.from(node.rows);
29818 trs.forEach(function(tr) {
29820 this.rows.push(row);
29824 Array.from(tr.cells).forEach(function(td) {
29827 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29828 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29829 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29830 html : td.innerHTML
29832 no_column += add.colspan;
29839 this.no_col = Math.max(this.no_col, no_column);
29846 normalizeRows: function()
29850 this.rows.forEach(function(row) {
29853 row = this.normalizeRow(row);
29855 row.forEach(function(c) {
29856 while (typeof(ret[rid][cid]) != 'undefined') {
29859 if (typeof(ret[rid]) == 'undefined') {
29865 if (c.rowspan < 2) {
29869 for(var i = 1 ;i < c.rowspan; i++) {
29870 if (typeof(ret[rid+i]) == 'undefined') {
29873 ret[rid+i][cid] = c;
29881 normalizeRow: function(row)
29884 row.forEach(function(c) {
29885 if (c.colspan < 2) {
29889 for(var i =0 ;i < c.colspan; i++) {
29897 deleteColumn : function(sel)
29899 if (!sel || sel.type != 'col') {
29902 if (this.no_col < 2) {
29906 this.rows.forEach(function(row) {
29907 var cols = this.normalizeRow(row);
29908 var col = cols[sel.col];
29909 if (col.colspan > 1) {
29919 removeColumn : function()
29921 this.deleteColumn({
29923 col : this.no_col-1
29925 this.updateElement();
29929 addColumn : function()
29932 this.rows.forEach(function(row) {
29933 row.push(this.emptyCell());
29936 this.updateElement();
29939 deleteRow : function(sel)
29941 if (!sel || sel.type != 'row') {
29945 if (this.no_row < 2) {
29949 var rows = this.normalizeRows();
29952 rows[sel.row].forEach(function(col) {
29953 if (col.rowspan > 1) {
29956 col.remove = 1; // flage it as removed.
29961 this.rows.forEach(function(row) {
29963 row.forEach(function(c) {
29964 if (typeof(c.remove) == 'undefined') {
29969 if (newrow.length > 0) {
29973 this.rows = newrows;
29978 this.updateElement();
29981 removeRow : function()
29985 row : this.no_row-1
29991 addRow : function()
29995 for (var i = 0; i < this.no_col; i++ ) {
29997 row.push(this.emptyCell());
30000 this.rows.push(row);
30001 this.updateElement();
30005 // the default cell object... at present...
30006 emptyCell : function() {
30007 return (new Roo.htmleditor.BlockTd({})).toObject();
30012 removeNode : function()
30019 resetWidths : function()
30021 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30022 var nn = Roo.htmleditor.Block.factory(n);
30024 nn.updateElement(n);
30037 * since selections really work on the table cell, then editing really should work from there
30039 * The original plan was to support merging etc... - but that may not be needed yet..
30041 * So this simple version will support:
30043 * adjust the width +/-
30044 * reset the width...
30053 * @class Roo.htmleditor.BlockTable
30054 * Block that manages a table
30057 * Create a new Filter.
30058 * @param {Object} config Configuration options
30061 Roo.htmleditor.BlockTd = function(cfg)
30064 this.readElement(cfg.node);
30065 this.updateElement(cfg.node);
30067 Roo.apply(this, cfg);
30072 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30077 textAlign : 'left',
30084 // used by context menu
30085 friendly_name : 'Table Cell',
30086 deleteTitle : false, // use our customer delete
30088 // context menu is drawn once..
30090 contextMenu : function(toolbar)
30093 var cell = function() {
30094 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30097 var table = function() {
30098 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30102 var saveSel = function()
30104 lr = toolbar.editorcore.getSelection().getRangeAt(0);
30106 var restoreSel = function()
30110 toolbar.editorcore.focus();
30111 var cr = toolbar.editorcore.getSelection();
30112 cr.removeAllRanges();
30114 toolbar.editorcore.onEditorEvent();
30115 }).defer(10, this);
30121 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30123 var syncValue = toolbar.editorcore.syncValue;
30130 text : 'Edit Table',
30132 click : function() {
30133 var t = toolbar.tb.selectedNode.closest('table');
30134 toolbar.editorcore.selectNode(t);
30135 toolbar.editorcore.onEditorEvent();
30144 xtype : 'TextItem',
30145 text : "Column Width: ",
30146 xns : rooui.Toolbar
30153 click : function (_self, e)
30155 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30156 cell().shrinkColumn();
30158 toolbar.editorcore.onEditorEvent();
30161 xns : rooui.Toolbar
30167 click : function (_self, e)
30169 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30170 cell().growColumn();
30172 toolbar.editorcore.onEditorEvent();
30175 xns : rooui.Toolbar
30179 xtype : 'TextItem',
30180 text : "Vertical Align: ",
30181 xns : rooui.Toolbar //Boostrap?
30184 xtype : 'ComboBox',
30185 allowBlank : false,
30186 displayField : 'val',
30189 triggerAction : 'all',
30191 valueField : 'val',
30195 select : function (combo, r, index)
30197 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30199 b.valign = r.get('val');
30202 toolbar.editorcore.onEditorEvent();
30207 xtype : 'SimpleStore',
30211 ['bottom'] // there are afew more...
30219 xtype : 'TextItem',
30220 text : "Merge Cells: ",
30221 xns : rooui.Toolbar
30230 click : function (_self, e)
30232 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30233 cell().mergeRight();
30234 //block().growColumn();
30236 toolbar.editorcore.onEditorEvent();
30239 xns : rooui.Toolbar
30246 click : function (_self, e)
30248 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30249 cell().mergeBelow();
30250 //block().growColumn();
30252 toolbar.editorcore.onEditorEvent();
30255 xns : rooui.Toolbar
30258 xtype : 'TextItem',
30260 xns : rooui.Toolbar
30268 click : function (_self, e)
30270 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30273 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30274 toolbar.editorcore.onEditorEvent();
30278 xns : rooui.Toolbar
30282 xns : rooui.Toolbar
30291 xns : rooui.Toolbar,
30300 click : function (_self, e)
30304 cell().deleteColumn();
30306 toolbar.editorcore.selectNode(t.node);
30307 toolbar.editorcore.onEditorEvent();
30316 click : function (_self, e)
30319 cell().deleteRow();
30322 toolbar.editorcore.selectNode(t.node);
30323 toolbar.editorcore.onEditorEvent();
30330 xtype : 'Separator',
30337 click : function (_self, e)
30340 var nn = t.node.nextSibling || t.node.previousSibling;
30341 t.node.parentNode.removeChild(t.node);
30343 toolbar.editorcore.selectNode(nn, true);
30345 toolbar.editorcore.onEditorEvent();
30355 // align... << fixme
30363 * create a DomHelper friendly object - for use with
30364 * Roo.DomHelper.markup / overwrite / etc..
30365 * ?? should it be called with option to hide all editing features?
30368 * create a DomHelper friendly object - for use with
30369 * Roo.DomHelper.markup / overwrite / etc..
30370 * ?? should it be called with option to hide all editing features?
30372 toObject : function()
30376 contenteditable : 'true', // this stops cell selection from picking the table.
30377 'data-block' : 'Td',
30378 valign : this.valign,
30380 'text-align' : this.textAlign,
30381 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30382 'border-collapse' : 'collapse',
30383 padding : '6px', // 8 for desktop / 4 for mobile
30384 'vertical-align': this.valign
30388 if (this.width != '') {
30389 ret.width = this.width;
30390 ret.style.width = this.width;
30394 if (this.colspan > 1) {
30395 ret.colspan = this.colspan ;
30397 if (this.rowspan > 1) {
30398 ret.rowspan = this.rowspan ;
30407 readElement : function(node)
30409 node = node ? node : this.node ;
30410 this.width = node.style.width;
30411 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30412 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30413 this.html = node.innerHTML;
30414 if (node.style.textAlign != '') {
30415 this.textAlign = node.style.textAlign;
30421 // the default cell object... at present...
30422 emptyCell : function() {
30426 textAlign : 'left',
30427 html : " " // is this going to be editable now?
30432 removeNode : function()
30434 return this.node.closest('table');
30442 toTableArray : function()
30445 var tab = this.node.closest('tr').closest('table');
30446 Array.from(tab.rows).forEach(function(r, ri){
30450 this.colWidths = [];
30451 var all_auto = true;
30452 Array.from(tab.rows).forEach(function(r, ri){
30455 Array.from(r.cells).forEach(function(ce, ci){
30460 colspan : ce.colSpan,
30461 rowspan : ce.rowSpan
30463 if (ce.isEqualNode(this.node)) {
30466 // if we have been filled up by a row?
30467 if (typeof(ret[rn][cn]) != 'undefined') {
30468 while(typeof(ret[rn][cn]) != 'undefined') {
30474 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30475 this.colWidths[cn] = ce.style.width;
30476 if (this.colWidths[cn] != '') {
30482 if (c.colspan < 2 && c.rowspan < 2 ) {
30487 for(var j = 0; j < c.rowspan; j++) {
30488 if (typeof(ret[rn+j]) == 'undefined') {
30489 continue; // we have a problem..
30492 for(var i = 0; i < c.colspan; i++) {
30493 ret[rn+j][cn+i] = c;
30502 // initalize widths.?
30503 // either all widths or no widths..
30505 this.colWidths[0] = false; // no widths flag.
30516 mergeRight: function()
30519 // get the contents of the next cell along..
30520 var tr = this.node.closest('tr');
30521 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30522 if (i >= tr.childNodes.length - 1) {
30523 return; // no cells on right to merge with.
30525 var table = this.toTableArray();
30527 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30528 return; // nothing right?
30530 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30531 // right cell - must be same rowspan and on the same row.
30532 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30533 return; // right hand side is not same rowspan.
30538 this.node.innerHTML += ' ' + rc.cell.innerHTML;
30539 tr.removeChild(rc.cell);
30540 this.colspan += rc.colspan;
30541 this.node.setAttribute('colspan', this.colspan);
30543 var table = this.toTableArray();
30544 this.normalizeWidths(table);
30545 this.updateWidths(table);
30549 mergeBelow : function()
30551 var table = this.toTableArray();
30552 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30553 return; // no row below
30555 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30556 return; // nothing right?
30558 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30560 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30561 return; // right hand side is not same rowspan.
30563 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30564 rc.cell.parentNode.removeChild(rc.cell);
30565 this.rowspan += rc.rowspan;
30566 this.node.setAttribute('rowspan', this.rowspan);
30571 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30574 var table = this.toTableArray();
30575 var cd = this.cellData;
30579 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30582 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30583 if (r == cd.row && c == cd.col) {
30584 this.node.removeAttribute('rowspan');
30585 this.node.removeAttribute('colspan');
30588 var ntd = this.node.cloneNode(); // which col/row should be 0..
30589 ntd.removeAttribute('id');
30590 ntd.style.width = this.colWidths[c];
30591 ntd.innerHTML = '';
30592 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30596 this.redrawAllCells(table);
30602 redrawAllCells: function(table)
30606 var tab = this.node.closest('tr').closest('table');
30607 var ctr = tab.rows[0].parentNode;
30608 Array.from(tab.rows).forEach(function(r, ri){
30610 Array.from(r.cells).forEach(function(ce, ci){
30611 ce.parentNode.removeChild(ce);
30613 r.parentNode.removeChild(r);
30615 for(var r = 0 ; r < table.length; r++) {
30616 var re = tab.rows[r];
30618 var re = tab.ownerDocument.createElement('tr');
30619 ctr.appendChild(re);
30620 for(var c = 0 ; c < table[r].length; c++) {
30621 if (table[r][c].cell === false) {
30625 re.appendChild(table[r][c].cell);
30627 table[r][c].cell = false;
30632 updateWidths : function(table)
30634 for(var r = 0 ; r < table.length; r++) {
30636 for(var c = 0 ; c < table[r].length; c++) {
30637 if (table[r][c].cell === false) {
30641 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30642 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30643 el.width = Math.floor(this.colWidths[c]) +'%';
30644 el.updateElement(el.node);
30646 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30647 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30649 for(var i = 0; i < table[r][c].colspan; i ++) {
30650 width += Math.floor(this.colWidths[c + i]);
30652 el.width = width +'%';
30653 el.updateElement(el.node);
30655 table[r][c].cell = false; // done
30659 normalizeWidths : function(table)
30661 if (this.colWidths[0] === false) {
30662 var nw = 100.0 / this.colWidths.length;
30663 this.colWidths.forEach(function(w,i) {
30664 this.colWidths[i] = nw;
30669 var t = 0, missing = [];
30671 this.colWidths.forEach(function(w,i) {
30673 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30674 var add = this.colWidths[i];
30683 var nc = this.colWidths.length;
30684 if (missing.length) {
30685 var mult = (nc - missing.length) / (1.0 * nc);
30687 var ew = (100 -t) / (1.0 * missing.length);
30688 this.colWidths.forEach(function(w,i) {
30690 this.colWidths[i] = w * mult;
30694 this.colWidths[i] = ew;
30696 // have to make up numbers..
30699 // now we should have all the widths..
30704 shrinkColumn : function()
30706 var table = this.toTableArray();
30707 this.normalizeWidths(table);
30708 var col = this.cellData.col;
30709 var nw = this.colWidths[col] * 0.8;
30713 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30714 this.colWidths.forEach(function(w,i) {
30716 this.colWidths[i] = nw;
30719 this.colWidths[i] += otherAdd
30721 this.updateWidths(table);
30724 growColumn : function()
30726 var table = this.toTableArray();
30727 this.normalizeWidths(table);
30728 var col = this.cellData.col;
30729 var nw = this.colWidths[col] * 1.2;
30733 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30734 this.colWidths.forEach(function(w,i) {
30736 this.colWidths[i] = nw;
30739 this.colWidths[i] -= otherSub
30741 this.updateWidths(table);
30744 deleteRow : function()
30746 // delete this rows 'tr'
30747 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30748 // then reduce the rowspan.
30749 var table = this.toTableArray();
30750 // this.cellData.row;
30751 for (var i =0;i< table[this.cellData.row].length ; i++) {
30752 var c = table[this.cellData.row][i];
30753 if (c.row != this.cellData.row) {
30756 c.cell.setAttribute('rowspan', c.rowspan);
30759 if (c.rowspan > 1) {
30761 c.cell.setAttribute('rowspan', c.rowspan);
30764 table.splice(this.cellData.row,1);
30765 this.redrawAllCells(table);
30768 deleteColumn : function()
30770 var table = this.toTableArray();
30772 for (var i =0;i< table.length ; i++) {
30773 var c = table[i][this.cellData.col];
30774 if (c.col != this.cellData.col) {
30775 table[i][this.cellData.col].colspan--;
30776 } else if (c.colspan > 1) {
30778 c.cell.setAttribute('colspan', c.colspan);
30780 table[i].splice(this.cellData.col,1);
30783 this.redrawAllCells(table);
30791 //<script type="text/javascript">
30794 * Based Ext JS Library 1.1.1
30795 * Copyright(c) 2006-2007, Ext JS, LLC.
30801 * @class Roo.HtmlEditorCore
30802 * @extends Roo.Component
30803 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30805 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30808 Roo.HtmlEditorCore = function(config){
30811 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30816 * @event initialize
30817 * Fires when the editor is fully initialized (including the iframe)
30818 * @param {Roo.HtmlEditorCore} this
30823 * Fires when the editor is first receives the focus. Any insertion must wait
30824 * until after this event.
30825 * @param {Roo.HtmlEditorCore} this
30829 * @event beforesync
30830 * Fires before the textarea is updated with content from the editor iframe. Return false
30831 * to cancel the sync.
30832 * @param {Roo.HtmlEditorCore} this
30833 * @param {String} html
30837 * @event beforepush
30838 * Fires before the iframe editor is updated with content from the textarea. Return false
30839 * to cancel the push.
30840 * @param {Roo.HtmlEditorCore} this
30841 * @param {String} html
30846 * Fires when the textarea is updated with content from the editor iframe.
30847 * @param {Roo.HtmlEditorCore} this
30848 * @param {String} html
30853 * Fires when the iframe editor is updated with content from the textarea.
30854 * @param {Roo.HtmlEditorCore} this
30855 * @param {String} html
30860 * @event editorevent
30861 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30862 * @param {Roo.HtmlEditorCore} this
30869 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30871 // defaults : white / black...
30872 this.applyBlacklists();
30879 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30883 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30889 * @cfg {String} css styling for resizing. (used on bootstrap only)
30893 * @cfg {Number} height (in pixels)
30897 * @cfg {Number} width (in pixels)
30901 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30902 * if you are doing an email editor, this probably needs disabling, it's designed
30907 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30909 enableBlocks : true,
30911 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30914 stylesheets: false,
30916 * @cfg {String} language default en - language of text (usefull for rtl languages)
30922 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30923 * - by default they are stripped - if you are editing email you may need this.
30925 allowComments: false,
30929 // private properties
30930 validationEvent : false,
30932 initialized : false,
30934 sourceEditMode : false,
30935 onFocus : Roo.emptyFn,
30937 hideMode:'offsets',
30941 // blacklist + whitelisted elements..
30948 undoManager : false,
30950 * Protected method that will not generally be called directly. It
30951 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30952 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30954 getDocMarkup : function(){
30958 // inherit styels from page...??
30959 if (this.stylesheets === false) {
30961 Roo.get(document.head).select('style').each(function(node) {
30962 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30965 Roo.get(document.head).select('link').each(function(node) {
30966 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30969 } else if (!this.stylesheets.length) {
30971 st = '<style type="text/css">' +
30972 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30975 for (var i in this.stylesheets) {
30976 if (typeof(this.stylesheets[i]) != 'string') {
30979 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30984 st += '<style type="text/css">' +
30985 'IMG { cursor: pointer } ' +
30988 st += '<meta name="google" content="notranslate">';
30990 var cls = 'notranslate roo-htmleditor-body';
30992 if(this.bodyCls.length){
30993 cls += ' ' + this.bodyCls;
30996 return '<html class="notranslate" translate="no"><head>' + st +
30997 //<style type="text/css">' +
30998 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
31000 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
31004 onRender : function(ct, position)
31007 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31008 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31011 this.el.dom.style.border = '0 none';
31012 this.el.dom.setAttribute('tabIndex', -1);
31013 this.el.addClass('x-hidden hide');
31017 if(Roo.isIE){ // fix IE 1px bogus margin
31018 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31022 this.frameId = Roo.id();
31026 cls: 'form-control', // bootstrap..
31028 name: this.frameId,
31029 frameBorder : 'no',
31030 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
31033 ifcfg.style = { resize : this.resize };
31036 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
31039 this.iframe = iframe.dom;
31041 this.assignDocWin();
31043 this.doc.designMode = 'on';
31046 this.doc.write(this.getDocMarkup());
31050 var task = { // must defer to wait for browser to be ready
31052 //console.log("run task?" + this.doc.readyState);
31053 this.assignDocWin();
31054 if(this.doc.body || this.doc.readyState == 'complete'){
31056 this.doc.designMode="on";
31061 Roo.TaskMgr.stop(task);
31062 this.initEditor.defer(10, this);
31069 Roo.TaskMgr.start(task);
31074 onResize : function(w, h)
31076 Roo.log('resize: ' +w + ',' + h );
31077 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31081 if(typeof w == 'number'){
31083 this.iframe.style.width = w + 'px';
31085 if(typeof h == 'number'){
31087 this.iframe.style.height = h + 'px';
31089 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31096 * Toggles the editor between standard and source edit mode.
31097 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31099 toggleSourceEdit : function(sourceEditMode){
31101 this.sourceEditMode = sourceEditMode === true;
31103 if(this.sourceEditMode){
31105 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
31108 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31109 //this.iframe.className = '';
31112 //this.setSize(this.owner.wrap.getSize());
31113 //this.fireEvent('editmodechange', this, this.sourceEditMode);
31120 * Protected method that will not generally be called directly. If you need/want
31121 * custom HTML cleanup, this is the method you should override.
31122 * @param {String} html The HTML to be cleaned
31123 * return {String} The cleaned HTML
31125 cleanHtml : function(html)
31127 html = String(html);
31128 if(html.length > 5){
31129 if(Roo.isSafari){ // strip safari nonsense
31130 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31133 if(html == ' '){
31140 * HTML Editor -> Textarea
31141 * Protected method that will not generally be called directly. Syncs the contents
31142 * of the editor iframe with the textarea.
31144 syncValue : function()
31146 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31147 if(this.initialized){
31149 if (this.undoManager) {
31150 this.undoManager.addEvent();
31154 var bd = (this.doc.body || this.doc.documentElement);
31157 var sel = this.win.getSelection();
31159 var div = document.createElement('div');
31160 div.innerHTML = bd.innerHTML;
31161 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31162 if (gtx.length > 0) {
31163 var rm = gtx.item(0).parentNode;
31164 rm.parentNode.removeChild(rm);
31168 if (this.enableBlocks) {
31169 new Roo.htmleditor.FilterBlock({ node : div });
31172 var html = div.innerHTML;
31175 if (this.autoClean) {
31177 new Roo.htmleditor.FilterAttributes({
31199 attrib_clean : ['href', 'src' ]
31202 var tidy = new Roo.htmleditor.TidySerializer({
31205 html = tidy.serialize(div);
31211 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31212 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31214 html = '<div style="'+m[0]+'">' + html + '</div>';
31217 html = this.cleanHtml(html);
31218 // fix up the special chars.. normaly like back quotes in word...
31219 // however we do not want to do this with chinese..
31220 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31222 var cc = match.charCodeAt();
31224 // Get the character value, handling surrogate pairs
31225 if (match.length == 2) {
31226 // It's a surrogate pair, calculate the Unicode code point
31227 var high = match.charCodeAt(0) - 0xD800;
31228 var low = match.charCodeAt(1) - 0xDC00;
31229 cc = (high * 0x400) + low + 0x10000;
31231 (cc >= 0x4E00 && cc < 0xA000 ) ||
31232 (cc >= 0x3400 && cc < 0x4E00 ) ||
31233 (cc >= 0xf900 && cc < 0xfb00 )
31238 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31239 return "&#" + cc + ";";
31246 if(this.owner.fireEvent('beforesync', this, html) !== false){
31247 this.el.dom.value = html;
31248 this.owner.fireEvent('sync', this, html);
31254 * TEXTAREA -> EDITABLE
31255 * Protected method that will not generally be called directly. Pushes the value of the textarea
31256 * into the iframe editor.
31258 pushValue : function()
31260 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31261 if(this.initialized){
31262 var v = this.el.dom.value.trim();
31265 if(this.owner.fireEvent('beforepush', this, v) !== false){
31266 var d = (this.doc.body || this.doc.documentElement);
31269 this.el.dom.value = d.innerHTML;
31270 this.owner.fireEvent('push', this, v);
31272 if (this.autoClean) {
31273 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31274 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31276 if (this.enableBlocks) {
31277 Roo.htmleditor.Block.initAll(this.doc.body);
31280 this.updateLanguage();
31282 var lc = this.doc.body.lastChild;
31283 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31284 // add an extra line at the end.
31285 this.doc.body.appendChild(this.doc.createElement('br'));
31293 deferFocus : function(){
31294 this.focus.defer(10, this);
31298 focus : function(){
31299 if(this.win && !this.sourceEditMode){
31306 assignDocWin: function()
31308 var iframe = this.iframe;
31311 this.doc = iframe.contentWindow.document;
31312 this.win = iframe.contentWindow;
31314 // if (!Roo.get(this.frameId)) {
31317 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31318 // this.win = Roo.get(this.frameId).dom.contentWindow;
31320 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31324 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31325 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31330 initEditor : function(){
31331 //console.log("INIT EDITOR");
31332 this.assignDocWin();
31336 this.doc.designMode="on";
31338 this.doc.write(this.getDocMarkup());
31341 var dbody = (this.doc.body || this.doc.documentElement);
31342 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31343 // this copies styles from the containing element into thsi one..
31344 // not sure why we need all of this..
31345 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31347 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31348 //ss['background-attachment'] = 'fixed'; // w3c
31349 dbody.bgProperties = 'fixed'; // ie
31350 dbody.setAttribute("translate", "no");
31352 //Roo.DomHelper.applyStyles(dbody, ss);
31353 Roo.EventManager.on(this.doc, {
31355 'mouseup': this.onEditorEvent,
31356 'dblclick': this.onEditorEvent,
31357 'click': this.onEditorEvent,
31358 'keyup': this.onEditorEvent,
31363 Roo.EventManager.on(this.doc, {
31364 'paste': this.onPasteEvent,
31368 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31371 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31372 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31374 this.initialized = true;
31377 // initialize special key events - enter
31378 new Roo.htmleditor.KeyEnter({core : this});
31382 this.owner.fireEvent('initialize', this);
31385 // this is to prevent a href clicks resulting in a redirect?
31387 onPasteEvent : function(e,v)
31389 // I think we better assume paste is going to be a dirty load of rubish from word..
31391 // even pasting into a 'email version' of this widget will have to clean up that mess.
31392 var cd = (e.browserEvent.clipboardData || window.clipboardData);
31394 // check what type of paste - if it's an image, then handle it differently.
31395 if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31397 var urlAPI = (window.createObjectURL && window) ||
31398 (window.URL && URL.revokeObjectURL && URL) ||
31399 (window.webkitURL && webkitURL);
31401 var r = new FileReader();
31403 r.addEventListener('load',function()
31406 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31407 // is insert asycn?
31408 if (t.enableBlocks) {
31410 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31411 if (img.closest('figure')) { // assume!! that it's aready
31414 var fig = new Roo.htmleditor.BlockFigure({
31415 image_src : img.src
31417 fig.updateElement(img); // replace it..
31421 t.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31422 t.owner.fireEvent('paste', this);
31424 r.readAsDataURL(cd.files[0]);
31426 e.preventDefault();
31430 if (cd.types.indexOf('text/html') < 0 ) {
31434 var html = cd.getData('text/html'); // clipboard event
31435 if (cd.types.indexOf('text/rtf') > -1) {
31436 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31437 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31442 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31443 .map(function(g) { return g.toDataURL(); })
31444 .filter(function(g) { return g != 'about:blank'; });
31447 html = this.cleanWordChars(html);
31449 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31452 var sn = this.getParentElement();
31453 // check if d contains a table, and prevent nesting??
31454 //Roo.log(d.getElementsByTagName('table'));
31456 //Roo.log(sn.closest('table'));
31457 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31458 e.preventDefault();
31459 this.insertAtCursor("You can not nest tables");
31460 //Roo.log("prevent?"); // fixme -
31466 if (images.length > 0) {
31467 // replace all v:imagedata - with img.
31468 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31469 Roo.each(ar, function(node) {
31470 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31471 node.parentNode.removeChild(node);
31475 Roo.each(d.getElementsByTagName('img'), function(img, i) {
31476 img.setAttribute('src', images[i]);
31479 if (this.autoClean) {
31480 new Roo.htmleditor.FilterWord({ node : d });
31482 new Roo.htmleditor.FilterStyleToTag({ node : d });
31483 new Roo.htmleditor.FilterAttributes({
31485 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31486 attrib_clean : ['href', 'src' ]
31488 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31489 // should be fonts..
31490 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31491 new Roo.htmleditor.FilterParagraph({ node : d });
31492 new Roo.htmleditor.FilterSpan({ node : d });
31493 new Roo.htmleditor.FilterLongBr({ node : d });
31494 new Roo.htmleditor.FilterComment({ node : d });
31498 if (this.enableBlocks) {
31500 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31501 if (img.closest('figure')) { // assume!! that it's aready
31504 var fig = new Roo.htmleditor.BlockFigure({
31505 image_src : img.src
31507 fig.updateElement(img); // replace it..
31513 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31514 if (this.enableBlocks) {
31515 Roo.htmleditor.Block.initAll(this.doc.body);
31519 e.preventDefault();
31520 this.owner.fireEvent('paste', this);
31522 // default behaveiour should be our local cleanup paste? (optional?)
31523 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31524 //this.owner.fireEvent('paste', e, v);
31527 onDestroy : function(){
31533 //for (var i =0; i < this.toolbars.length;i++) {
31534 // // fixme - ask toolbars for heights?
31535 // this.toolbars[i].onDestroy();
31538 //this.wrap.dom.innerHTML = '';
31539 //this.wrap.remove();
31544 onFirstFocus : function(){
31546 this.assignDocWin();
31547 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31549 this.activated = true;
31552 if(Roo.isGecko){ // prevent silly gecko errors
31554 var s = this.win.getSelection();
31555 if(!s.focusNode || s.focusNode.nodeType != 3){
31556 var r = s.getRangeAt(0);
31557 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31562 this.execCmd('useCSS', true);
31563 this.execCmd('styleWithCSS', false);
31566 this.owner.fireEvent('activate', this);
31570 adjustFont: function(btn){
31571 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31572 //if(Roo.isSafari){ // safari
31575 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31576 if(Roo.isSafari){ // safari
31577 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31578 v = (v < 10) ? 10 : v;
31579 v = (v > 48) ? 48 : v;
31580 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31585 v = Math.max(1, v+adjust);
31587 this.execCmd('FontSize', v );
31590 onEditorEvent : function(e)
31594 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31595 return; // we do not handle this.. (undo manager does..)
31597 // clicking a 'block'?
31599 // in theory this detects if the last element is not a br, then we try and do that.
31600 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31602 e.target.nodeName == 'BODY' &&
31603 e.type == "mouseup" &&
31604 this.doc.body.lastChild
31606 var lc = this.doc.body.lastChild;
31607 // gtx-trans is google translate plugin adding crap.
31608 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31609 lc = lc.previousSibling;
31611 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31612 // if last element is <BR> - then dont do anything.
31614 var ns = this.doc.createElement('br');
31615 this.doc.body.appendChild(ns);
31616 range = this.doc.createRange();
31617 range.setStartAfter(ns);
31618 range.collapse(true);
31619 var sel = this.win.getSelection();
31620 sel.removeAllRanges();
31621 sel.addRange(range);
31627 this.fireEditorEvent(e);
31628 // this.updateToolbar();
31629 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31632 fireEditorEvent: function(e)
31634 this.owner.fireEvent('editorevent', this, e);
31637 insertTag : function(tg)
31639 // could be a bit smarter... -> wrap the current selected tRoo..
31640 if (tg.toLowerCase() == 'span' ||
31641 tg.toLowerCase() == 'code' ||
31642 tg.toLowerCase() == 'sup' ||
31643 tg.toLowerCase() == 'sub'
31646 range = this.createRange(this.getSelection());
31647 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31648 wrappingNode.appendChild(range.extractContents());
31649 range.insertNode(wrappingNode);
31656 this.execCmd("formatblock", tg);
31657 this.undoManager.addEvent();
31660 insertText : function(txt)
31664 var range = this.createRange();
31665 range.deleteContents();
31666 //alert(Sender.getAttribute('label'));
31668 range.insertNode(this.doc.createTextNode(txt));
31669 this.undoManager.addEvent();
31675 * Executes a Midas editor command on the editor document and performs necessary focus and
31676 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31677 * @param {String} cmd The Midas command
31678 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31680 relayCmd : function(cmd, value)
31684 case 'justifyleft':
31685 case 'justifyright':
31686 case 'justifycenter':
31687 // if we are in a cell, then we will adjust the
31688 var n = this.getParentElement();
31689 var td = n.closest('td');
31691 var bl = Roo.htmleditor.Block.factory(td);
31692 bl.textAlign = cmd.replace('justify','');
31693 bl.updateElement();
31694 this.owner.fireEvent('editorevent', this);
31697 this.execCmd('styleWithCSS', true); //
31702 // if there is no selection, then we insert, and set the curson inside it..
31703 this.execCmd('styleWithCSS', false);
31713 this.execCmd(cmd, value);
31714 this.owner.fireEvent('editorevent', this);
31715 //this.updateToolbar();
31716 this.owner.deferFocus();
31720 * Executes a Midas editor command directly on the editor document.
31721 * For visual commands, you should use {@link #relayCmd} instead.
31722 * <b>This should only be called after the editor is initialized.</b>
31723 * @param {String} cmd The Midas command
31724 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31726 execCmd : function(cmd, value){
31727 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31734 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31736 * @param {String} text | dom node..
31738 insertAtCursor : function(text)
31741 if(!this.activated){
31745 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31749 // from jquery ui (MIT licenced)
31751 var win = this.win;
31753 if (win.getSelection && win.getSelection().getRangeAt) {
31755 // delete the existing?
31757 this.createRange(this.getSelection()).deleteContents();
31758 range = win.getSelection().getRangeAt(0);
31759 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31760 range.insertNode(node);
31761 range = range.cloneRange();
31762 range.collapse(false);
31764 win.getSelection().removeAllRanges();
31765 win.getSelection().addRange(range);
31769 } else if (win.document.selection && win.document.selection.createRange) {
31770 // no firefox support
31771 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31772 win.document.selection.createRange().pasteHTML(txt);
31775 // no firefox support
31776 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31777 this.execCmd('InsertHTML', txt);
31785 mozKeyPress : function(e){
31787 var c = e.getCharCode(), cmd;
31790 c = String.fromCharCode(c).toLowerCase();
31804 // this.cleanUpPaste.defer(100, this);
31810 this.relayCmd(cmd);
31811 //this.win.focus();
31812 //this.execCmd(cmd);
31813 //this.deferFocus();
31814 e.preventDefault();
31822 fixKeys : function(){ // load time branching for fastest keydown performance
31826 return function(e){
31827 var k = e.getKey(), r;
31830 r = this.doc.selection.createRange();
31833 r.pasteHTML('    ');
31838 /// this is handled by Roo.htmleditor.KeyEnter
31841 r = this.doc.selection.createRange();
31843 var target = r.parentElement();
31844 if(!target || target.tagName.toLowerCase() != 'li'){
31846 r.pasteHTML('<br/>');
31853 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31854 // this.cleanUpPaste.defer(100, this);
31860 }else if(Roo.isOpera){
31861 return function(e){
31862 var k = e.getKey();
31866 this.execCmd('InsertHTML','    ');
31870 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31871 // this.cleanUpPaste.defer(100, this);
31876 }else if(Roo.isSafari){
31877 return function(e){
31878 var k = e.getKey();
31882 this.execCmd('InsertText','\t');
31886 this.mozKeyPress(e);
31888 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31889 // this.cleanUpPaste.defer(100, this);
31897 getAllAncestors: function()
31899 var p = this.getSelectedNode();
31902 a.push(p); // push blank onto stack..
31903 p = this.getParentElement();
31907 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31911 a.push(this.doc.body);
31915 lastSelNode : false,
31918 getSelection : function()
31920 this.assignDocWin();
31921 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31924 * Select a dom node
31925 * @param {DomElement} node the node to select
31927 selectNode : function(node, collapse)
31929 var nodeRange = node.ownerDocument.createRange();
31931 nodeRange.selectNode(node);
31933 nodeRange.selectNodeContents(node);
31935 if (collapse === true) {
31936 nodeRange.collapse(true);
31939 var s = this.win.getSelection();
31940 s.removeAllRanges();
31941 s.addRange(nodeRange);
31944 getSelectedNode: function()
31946 // this may only work on Gecko!!!
31948 // should we cache this!!!!
31952 var range = this.createRange(this.getSelection()).cloneRange();
31955 var parent = range.parentElement();
31957 var testRange = range.duplicate();
31958 testRange.moveToElementText(parent);
31959 if (testRange.inRange(range)) {
31962 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31965 parent = parent.parentElement;
31970 // is ancestor a text element.
31971 var ac = range.commonAncestorContainer;
31972 if (ac.nodeType == 3) {
31973 ac = ac.parentNode;
31976 var ar = ac.childNodes;
31979 var other_nodes = [];
31980 var has_other_nodes = false;
31981 for (var i=0;i<ar.length;i++) {
31982 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31985 // fullly contained node.
31987 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31992 // probably selected..
31993 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31994 other_nodes.push(ar[i]);
31998 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
32003 has_other_nodes = true;
32005 if (!nodes.length && other_nodes.length) {
32006 nodes= other_nodes;
32008 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32016 createRange: function(sel)
32018 // this has strange effects when using with
32019 // top toolbar - not sure if it's a great idea.
32020 //this.editor.contentWindow.focus();
32021 if (typeof sel != "undefined") {
32023 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32025 return this.doc.createRange();
32028 return this.doc.createRange();
32031 getParentElement: function()
32034 this.assignDocWin();
32035 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32037 var range = this.createRange(sel);
32040 var p = range.commonAncestorContainer;
32041 while (p.nodeType == 3) { // text node
32052 * Range intersection.. the hard stuff...
32056 * [ -- selected range --- ]
32060 * if end is before start or hits it. fail.
32061 * if start is after end or hits it fail.
32063 * if either hits (but other is outside. - then it's not
32069 // @see http://www.thismuchiknow.co.uk/?p=64.
32070 rangeIntersectsNode : function(range, node)
32072 var nodeRange = node.ownerDocument.createRange();
32074 nodeRange.selectNode(node);
32076 nodeRange.selectNodeContents(node);
32079 var rangeStartRange = range.cloneRange();
32080 rangeStartRange.collapse(true);
32082 var rangeEndRange = range.cloneRange();
32083 rangeEndRange.collapse(false);
32085 var nodeStartRange = nodeRange.cloneRange();
32086 nodeStartRange.collapse(true);
32088 var nodeEndRange = nodeRange.cloneRange();
32089 nodeEndRange.collapse(false);
32091 return rangeStartRange.compareBoundaryPoints(
32092 Range.START_TO_START, nodeEndRange) == -1 &&
32093 rangeEndRange.compareBoundaryPoints(
32094 Range.START_TO_START, nodeStartRange) == 1;
32098 rangeCompareNode : function(range, node)
32100 var nodeRange = node.ownerDocument.createRange();
32102 nodeRange.selectNode(node);
32104 nodeRange.selectNodeContents(node);
32108 range.collapse(true);
32110 nodeRange.collapse(true);
32112 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32113 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
32115 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32117 var nodeIsBefore = ss == 1;
32118 var nodeIsAfter = ee == -1;
32120 if (nodeIsBefore && nodeIsAfter) {
32123 if (!nodeIsBefore && nodeIsAfter) {
32124 return 1; //right trailed.
32127 if (nodeIsBefore && !nodeIsAfter) {
32128 return 2; // left trailed.
32134 cleanWordChars : function(input) {// change the chars to hex code
32137 [ 8211, "–" ],
32138 [ 8212, "—" ],
32146 var output = input;
32147 Roo.each(swapCodes, function(sw) {
32148 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32150 output = output.replace(swapper, sw[1]);
32160 cleanUpChild : function (node)
32163 new Roo.htmleditor.FilterComment({node : node});
32164 new Roo.htmleditor.FilterAttributes({
32166 attrib_black : this.ablack,
32167 attrib_clean : this.aclean,
32168 style_white : this.cwhite,
32169 style_black : this.cblack
32171 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32172 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32178 * Clean up MS wordisms...
32179 * @deprecated - use filter directly
32181 cleanWord : function(node)
32183 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32184 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32191 * @deprecated - use filters
32193 cleanTableWidths : function(node)
32195 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32202 applyBlacklists : function()
32204 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
32205 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
32207 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
32208 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
32209 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
32213 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32214 if (b.indexOf(tag) > -1) {
32217 this.white.push(tag);
32221 Roo.each(w, function(tag) {
32222 if (b.indexOf(tag) > -1) {
32225 if (this.white.indexOf(tag) > -1) {
32228 this.white.push(tag);
32233 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32234 if (w.indexOf(tag) > -1) {
32237 this.black.push(tag);
32241 Roo.each(b, function(tag) {
32242 if (w.indexOf(tag) > -1) {
32245 if (this.black.indexOf(tag) > -1) {
32248 this.black.push(tag);
32253 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
32254 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
32258 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32259 if (b.indexOf(tag) > -1) {
32262 this.cwhite.push(tag);
32266 Roo.each(w, function(tag) {
32267 if (b.indexOf(tag) > -1) {
32270 if (this.cwhite.indexOf(tag) > -1) {
32273 this.cwhite.push(tag);
32278 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32279 if (w.indexOf(tag) > -1) {
32282 this.cblack.push(tag);
32286 Roo.each(b, function(tag) {
32287 if (w.indexOf(tag) > -1) {
32290 if (this.cblack.indexOf(tag) > -1) {
32293 this.cblack.push(tag);
32298 setStylesheets : function(stylesheets)
32300 if(typeof(stylesheets) == 'string'){
32301 Roo.get(this.iframe.contentDocument.head).createChild({
32303 rel : 'stylesheet',
32312 Roo.each(stylesheets, function(s) {
32317 Roo.get(_this.iframe.contentDocument.head).createChild({
32319 rel : 'stylesheet',
32329 updateLanguage : function()
32331 if (!this.iframe || !this.iframe.contentDocument) {
32334 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32338 removeStylesheets : function()
32342 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32347 setStyle : function(style)
32349 Roo.get(this.iframe.contentDocument.head).createChild({
32358 // hide stuff that is not compatible
32372 * @event specialkey
32376 * @cfg {String} fieldClass @hide
32379 * @cfg {String} focusClass @hide
32382 * @cfg {String} autoCreate @hide
32385 * @cfg {String} inputType @hide
32388 * @cfg {String} invalidClass @hide
32391 * @cfg {String} invalidText @hide
32394 * @cfg {String} msgFx @hide
32397 * @cfg {String} validateOnBlur @hide
32401 Roo.HtmlEditorCore.white = [
32402 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32404 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
32405 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
32406 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
32407 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
32408 'TABLE', 'UL', 'XMP',
32410 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
32413 'DIR', 'MENU', 'OL', 'UL', 'DL',
32419 Roo.HtmlEditorCore.black = [
32420 // 'embed', 'object', // enable - backend responsiblity to clean thiese
32422 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
32423 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
32424 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
32425 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
32426 //'FONT' // CLEAN LATER..
32427 'COLGROUP', 'COL' // messy tables.
32431 Roo.HtmlEditorCore.clean = [ // ?? needed???
32432 'SCRIPT', 'STYLE', 'TITLE', 'XML'
32434 Roo.HtmlEditorCore.tag_remove = [
32439 Roo.HtmlEditorCore.ablack = [
32443 Roo.HtmlEditorCore.aclean = [
32444 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
32448 Roo.HtmlEditorCore.pwhite= [
32449 'http', 'https', 'mailto'
32452 // white listed style attributes.
32453 Roo.HtmlEditorCore.cwhite= [
32454 // 'text-align', /// default is to allow most things..
32460 // black listed style attributes.
32461 Roo.HtmlEditorCore.cblack= [
32462 // 'font-size' -- this can be set by the project
32476 * @class Roo.bootstrap.form.HtmlEditor
32477 * @extends Roo.bootstrap.form.TextArea
32478 * Bootstrap HtmlEditor class
32481 * Create a new HtmlEditor
32482 * @param {Object} config The config object
32485 Roo.bootstrap.form.HtmlEditor = function(config){
32489 * @event initialize
32490 * Fires when the editor is fully initialized (including the iframe)
32491 * @param {Roo.bootstrap.form.HtmlEditor} this
32496 * Fires when the editor is first receives the focus. Any insertion must wait
32497 * until after this event.
32498 * @param {Roo.bootstrap.form.HtmlEditor} this
32502 * @event beforesync
32503 * Fires before the textarea is updated with content from the editor iframe. Return false
32504 * to cancel the sync.
32505 * @param {Roo.bootstrap.form.HtmlEditor} this
32506 * @param {String} html
32510 * @event beforepush
32511 * Fires before the iframe editor is updated with content from the textarea. Return false
32512 * to cancel the push.
32513 * @param {Roo.bootstrap.form.HtmlEditor} this
32514 * @param {String} html
32519 * Fires when the textarea is updated with content from the editor iframe.
32520 * @param {Roo.bootstrap.form.HtmlEditor} this
32521 * @param {String} html
32526 * Fires when the iframe editor is updated with content from the textarea.
32527 * @param {Roo.bootstrap.form.HtmlEditor} this
32528 * @param {String} html
32532 * @event editmodechange
32533 * Fires when the editor switches edit modes
32534 * @param {Roo.bootstrap.form.HtmlEditor} this
32535 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32537 editmodechange: true,
32539 * @event editorevent
32540 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32541 * @param {Roo.bootstrap.form.HtmlEditor} this
32545 * @event firstfocus
32546 * Fires when on first focus - needed by toolbars..
32547 * @param {Roo.bootstrap.form.HtmlEditor} this
32552 * Auto save the htmlEditor value as a file into Events
32553 * @param {Roo.bootstrap.form.HtmlEditor} this
32557 * @event savedpreview
32558 * preview the saved version of htmlEditor
32559 * @param {Roo.bootstrap.form.HtmlEditor} this
32561 savedpreview: true,
32563 * @event stylesheetsclick
32564 * Fires when press the Sytlesheets button
32565 * @param {Roo.HtmlEditorCore} this
32567 stylesheetsclick: true,
32570 * Fires when press user pastes into the editor
32571 * @param {Roo.HtmlEditorCore} this
32576 * Fires when on any editor when an image is added (excluding paste)
32577 * @param {Roo.bootstrap.form.HtmlEditor} this
32581 * @event imageupdated
32582 * Fires when on any editor when an image is changed (excluding paste)
32583 * @param {Roo.bootstrap.form.HtmlEditor} this
32584 * @param {HTMLElement} img could also be a figure if blocks are enabled
32586 imageupdate: true ,
32588 * @event imagedelete
32589 * Fires when on any editor when an image is deleted
32590 * @param {Roo.bootstrap.form.HtmlEditor} this
32591 * @param {HTMLElement} img could also be a figure if blocks are enabled
32595 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32596 if (!this.toolbars) {
32597 this.toolbars = [];
32600 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32605 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
32609 * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32614 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32619 * @cfg {String} resize (none|both|horizontal|vertical) - css resize of element
32623 * @cfg {Number} height (in pixels)
32627 * @cfg {Number} width (in pixels)
32632 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32635 stylesheets: false,
32640 // private properties
32641 validationEvent : false,
32643 initialized : false,
32646 onFocus : Roo.emptyFn,
32648 hideMode:'offsets',
32650 tbContainer : false,
32654 toolbarContainer :function() {
32655 return this.wrap.select('.x-html-editor-tb',true).first();
32659 * Protected method that will not generally be called directly. It
32660 * is called when the editor creates its toolbar. Override this method if you need to
32661 * add custom toolbar buttons.
32662 * @param {HtmlEditor} editor
32664 createToolbar : function()
32666 //Roo.log('renewing');
32667 //Roo.log("create toolbars");
32668 if (this.toolbars === false) {
32671 if (this.toolbars === true) {
32672 this.toolbars = [ 'Standard' ];
32675 var ar = Array.from(this.toolbars);
32676 this.toolbars = [];
32677 ar.forEach(function(t,i) {
32678 if (typeof(t) == 'string') {
32683 if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32685 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32686 t = Roo.factory(t);
32688 this.toolbars[i] = t;
32689 this.toolbars[i].render(this.toolbarContainer());
32697 onRender : function(ct, position)
32699 // Roo.log("Call onRender: " + this.xtype);
32701 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32703 this.wrap = this.inputEl().wrap({
32704 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32707 this.editorcore.onRender(ct, position);
32710 this.createToolbar(this);
32718 onResize : function(w, h)
32720 Roo.log('resize: ' +w + ',' + h );
32721 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32725 if(this.inputEl() ){
32726 if(typeof w == 'number'){
32727 var aw = w - this.wrap.getFrameWidth('lr');
32728 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32731 if(typeof h == 'number'){
32732 var tbh = -11; // fixme it needs to tool bar size!
32733 for (var i =0; i < this.toolbars.length;i++) {
32734 // fixme - ask toolbars for heights?
32735 tbh += this.toolbars[i].el.getHeight();
32736 //if (this.toolbars[i].footer) {
32737 // tbh += this.toolbars[i].footer.el.getHeight();
32745 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32746 ah -= 5; // knock a few pixes off for look..
32747 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32751 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32752 this.editorcore.onResize(ew,eh);
32757 * Toggles the editor between standard and source edit mode.
32758 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32760 toggleSourceEdit : function(sourceEditMode)
32762 this.editorcore.toggleSourceEdit(sourceEditMode);
32764 if(this.editorcore.sourceEditMode){
32765 Roo.log('editor - showing textarea');
32768 // Roo.log(this.syncValue());
32770 this.inputEl().removeClass(['hide', 'x-hidden']);
32771 this.inputEl().dom.removeAttribute('tabIndex');
32772 this.inputEl().focus();
32774 Roo.log('editor - hiding textarea');
32776 // Roo.log(this.pushValue());
32779 this.inputEl().addClass(['hide', 'x-hidden']);
32780 this.inputEl().dom.setAttribute('tabIndex', -1);
32781 //this.deferFocus();
32784 //if(this.resizable){
32785 // this.setSize(this.wrap.getSize());
32788 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32791 // private (for BoxComponent)
32792 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32794 // private (for BoxComponent)
32795 getResizeEl : function(){
32799 // private (for BoxComponent)
32800 getPositionEl : function(){
32805 initEvents : function(){
32806 this.originalValue = this.getValue();
32810 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32813 // markInvalid : Roo.emptyFn,
32815 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32818 // clearInvalid : Roo.emptyFn,
32820 setValue : function(v){
32821 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32822 this.editorcore.pushValue();
32827 deferFocus : function(){
32828 this.focus.defer(10, this);
32832 focus : function(){
32833 this.editorcore.focus();
32839 onDestroy : function(){
32845 for (var i =0; i < this.toolbars.length;i++) {
32846 // fixme - ask toolbars for heights?
32847 this.toolbars[i].onDestroy();
32850 this.wrap.dom.innerHTML = '';
32851 this.wrap.remove();
32856 onFirstFocus : function(){
32857 //Roo.log("onFirstFocus");
32858 this.editorcore.onFirstFocus();
32859 for (var i =0; i < this.toolbars.length;i++) {
32860 this.toolbars[i].onFirstFocus();
32866 syncValue : function()
32868 this.editorcore.syncValue();
32871 pushValue : function()
32873 this.editorcore.pushValue();
32877 // hide stuff that is not compatible
32891 * @event specialkey
32895 * @cfg {String} fieldClass @hide
32898 * @cfg {String} focusClass @hide
32901 * @cfg {String} autoCreate @hide
32904 * @cfg {String} inputType @hide
32908 * @cfg {String} invalidText @hide
32911 * @cfg {String} msgFx @hide
32914 * @cfg {String} validateOnBlur @hide
32924 * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32925 * @parent Roo.bootstrap.form.HtmlEditor
32926 * @extends Roo.bootstrap.nav.Simplebar
32932 new Roo.bootstrap.form.HtmlEditor({
32935 new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32936 disable : { fonts: 1 , format: 1, ..., ... , ...],
32942 * @cfg {Object} disable List of elements to disable..
32943 * @cfg {Array} btns List of additional buttons.
32947 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32950 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32953 Roo.apply(this, config);
32955 // default disabled, based on 'good practice'..
32956 this.disable = this.disable || {};
32957 Roo.applyIf(this.disable, {
32960 specialElements : true
32962 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32964 this.editor = config.editor;
32965 this.editorcore = config.editor.editorcore;
32967 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32969 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32970 // dont call parent... till later.
32972 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar, {
32977 editorcore : false,
32982 "h1","h2","h3","h4","h5","h6",
32984 "abbr", "acronym", "address", "cite", "samp", "var",
32991 onRender : function(ct, position)
32993 // Roo.log("Call onRender: " + this.xtype);
32995 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
32997 this.el.dom.style.marginBottom = '0';
32999 var editorcore = this.editorcore;
33000 var editor= this.editor;
33003 var btn = function(id, cmd , toggle, handler, html){
33005 var event = toggle ? 'toggle' : 'click';
33010 xns: Roo.bootstrap,
33014 cls : 'roo-html-editor-btn-' + id,
33015 cmd : cmd, // why id || cmd
33016 enableToggle: toggle !== false,
33018 pressed : toggle ? false : null,
33021 a.listeners[toggle ? 'toggle' : 'click'] = function() {
33022 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
33028 // var cb_box = function...
33033 xns: Roo.bootstrap,
33035 cls : 'roo-html-editor-font-chooser',
33039 xns: Roo.bootstrap,
33043 Roo.each(this.formats, function(f) {
33044 style.menu.items.push({
33046 xns: Roo.bootstrap,
33047 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33052 editorcore.insertTag(this.tagname);
33059 children.push(style);
33061 btn('bold', 'bold',true);
33062 btn('italic', 'italic',true);
33063 btn('underline', 'underline',true);
33064 btn('align-left', 'justifyleft',true);
33065 btn('align-center', 'justifycenter',true);
33066 btn('align-right' , 'justifyright',true);
33067 btn('link', false, true, this.onLinkClick);
33070 btn('image', false, true, this.onImageClick);
33071 btn('list','insertunorderedlist',true);
33072 btn('list-ol','insertorderedlist',true);
33074 btn('pencil', false,true, function(btn){
33076 this.toggleSourceEdit(btn.pressed);
33079 if (this.editor.btns.length > 0) {
33080 for (var i = 0; i<this.editor.btns.length; i++) {
33081 children.push(this.editor.btns[i]);
33087 this.xtype = 'NavSimplebar'; // why?
33089 for(var i=0;i< children.length;i++) {
33091 this.buttons.add(this.addxtypeChild(children[i]));
33094 this.buildToolbarDelete();
33096 editor.on('editorevent', this.updateToolbar, this);
33099 buildToolbarDelete : function()
33102 /* this.addxtypeChild({
33104 xns : Roo.bootstrap,
33105 cls : 'roo-htmleditor-fill'
33108 this.deleteBtn = this.addxtypeChild({
33111 xns: Roo.bootstrap,
33114 click : this.onDelete.createDelegate(this)
33117 this.deleteBtn.hide();
33121 onImageClick : function()
33124 this.input.un('change', this.onFileSelected, this);
33126 this.input = Roo.get(document.body).createChild({
33129 style : 'display:none',
33130 multiple: 'multiple'
33132 this.input.on('change', this.onFileSelected, this);
33133 this.input.dom.click();
33136 onFileSelected : function(e)
33138 e.preventDefault();
33140 if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33145 this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33148 addFiles : function(far, fire_add) {
33151 var editor = this.editorcore;
33155 this.editor.syncValue();
33156 editor.owner.fireEvent('editorevent', editor.owner, false);
33157 editor.owner.fireEvent('imageadd', editor.owner, false);
33164 if (!f.type.match(/^image/)) {
33165 this.addFiles(far, fire_add);
33169 var sn = this.selectedNode;
33171 var bl = sn && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33174 var reader = new FileReader();
33175 reader.addEventListener('load', (function() {
33177 bl.image_src = reader.result;
33178 //bl.caption = f.name;
33179 bl.updateElement(sn);
33180 this.editor.syncValue();
33181 editor.owner.fireEvent('editorevent', editor.owner, false);
33182 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33183 // we only do the first file!! and replace.
33186 if (this.editorcore.enableBlocks) {
33187 var fig = new Roo.htmleditor.BlockFigure({
33188 image_src : reader.result,
33190 caption_display : 'none' //default to hide captions..
33192 editor.insertAtCursor(fig.toHTML());
33193 this.addFiles(far, true);
33196 // just a standard img..
33197 if (sn && sn.tagName.toUpperCase() == 'IMG') {
33198 sn.src = reader.result;
33199 this.editor.syncValue();
33200 editor.owner.fireEvent('editorevent', editor.owner, false);
33201 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33204 editor.insertAtCursor('<img src="' + reader.result +'">');
33205 this.addFiles(far, true);
33207 }).createDelegate(this));
33208 reader.readAsDataURL(f);
33214 onBtnClick : function(id)
33216 this.editorcore.relayCmd(id);
33217 this.editorcore.focus();
33220 onLinkClick : function(btn) {
33221 var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33222 this.selectedNode.getAttribute('href') : '';
33224 Roo.bootstrap.MessageBox.show({
33225 title : "Add / Edit Link URL",
33226 msg : "Enter the URL for the link",
33227 buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33234 fn: function(pressed, newurl) {
33235 if (pressed != 'ok') {
33236 this.editorcore.focus();
33240 this.selectedNode.setAttribute('href', newurl);
33243 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33244 this.editorcore.relayCmd('createlink', newurl);
33246 this.editorcore.focus();
33251 * Protected method that will not generally be called directly. It triggers
33252 * a toolbar update by reading the markup state of the current selection in the editor.
33254 updateToolbar: function(editor ,ev, sel){
33256 if(!this.editorcore.activated){
33257 this.editor.onFirstFocus(); // is this neeed?
33261 var btns = this.buttons;
33262 var doc = this.editorcore.doc;
33263 var hasToggle = false;
33264 btns.each(function(e) {
33265 if (e.enableToggle && e.cmd) {
33266 hasToggle = hasToggle || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33267 e.setActive(doc.queryCommandState(e.cmd));
33273 (ev.type == 'mouseup' || ev.type == 'click' ) &&
33274 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33275 // they have click on an image...
33276 // let's see if we can change the selection...
33281 var ans = this.editorcore.getAllAncestors();
33283 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
33284 sel = sel ? sel : this.editorcore.doc.body;
33285 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33289 var lastSel = this.selectedNode;
33290 this.selectedNode = sel;
33292 // ok see if we are editing a block?
33295 // you are not actually selecting the block.
33296 if (sel && sel.hasAttribute('data-block')) {
33298 } else if (sel && sel.closest('[data-block]')) {
33299 db = sel.closest('[data-block]');
33302 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33303 e.classList.remove('roo-ed-selection');
33307 if (db && this.editorcore.enableBlocks) {
33308 block = Roo.htmleditor.Block.factory(db);
33311 db.className = (db.classList.length > 0 ? db.className + ' ' : '') +
33312 ' roo-ed-selection';
33313 sel = this.selectedNode = db;
33317 // highlight the 'a'..
33318 var tn = sel && sel.tagName.toUpperCase() || '';
33319 if (!block && sel && tn != 'A') {
33320 var asel = sel.closest('A');
33326 btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33327 btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33328 btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33330 Roo.bootstrap.menu.Manager.hideAll();
33336 // handle delete button..
33337 if (hasToggle || (tn.length && tn == 'BODY')) {
33338 this.deleteBtn.hide();
33342 this.deleteBtn.show();
33346 //this.editorsyncValue();
33348 onFirstFocus: function() {
33349 this.buttons.each(function(item){
33354 onDelete : function()
33356 var range = this.editorcore.createRange();
33357 var selection = this.editorcore.getSelection();
33358 var sn = this.selectedNode;
33359 range.setStart(sn,0);
33360 range.setEnd(sn,0);
33363 if (sn.hasAttribute('data-block')) {
33364 var block = Roo.htmleditor.Block.factory(this.selectedNode);
33366 sn = block.removeNode();
33367 sn.parentNode.removeChild(sn);
33368 selection.removeAllRanges();
33369 selection.addRange(range);
33370 this.updateToolbar(null, null, null);
33371 if (sn.tagName.toUpperCase() == 'FIGURE') {
33372 this.editor.syncValue();
33373 this.editor.fireEvent('imagedelete', this.editor, sn);
33376 this.selectedNode = false;
33377 this.editorcore.fireEditorEvent(false);
33383 return; // should not really happen..
33385 if (sn && sn.tagName == 'BODY') {
33388 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33390 // remove and keep parents.
33391 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33394 selection.removeAllRanges();
33395 selection.addRange(range);
33396 if (sn.tagName.toUpperCase() == 'IMG"') {
33397 this.editor.syncValue();
33398 this.editor.fireEvent('imagedelete', this.editor, sn);
33401 this.selectedNode = false;
33402 this.editorcore.fireEditorEvent(false);
33408 toggleSourceEdit : function(sourceEditMode){
33411 if(sourceEditMode){
33412 Roo.log("disabling buttons");
33413 this.buttons.each( function(item){
33414 if(item.cmd != 'pencil'){
33420 Roo.log("enabling buttons");
33421 if(this.editorcore.initialized){
33422 this.buttons.each( function(item){
33428 Roo.log("calling toggole on editor");
33429 // tell the editor that it's been pressed..
33430 this.editor.toggleSourceEdit(sourceEditMode);
33444 * @class Roo.bootstrap.form.Markdown
33445 * @extends Roo.bootstrap.form.TextArea
33446 * Bootstrap Showdown editable area
33447 * @cfg {string} content
33450 * Create a new Showdown
33453 Roo.bootstrap.form.Markdown = function(config){
33454 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33458 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
33462 initEvents : function()
33465 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33466 this.markdownEl = this.el.createChild({
33467 cls : 'roo-markdown-area'
33469 this.inputEl().addClass('d-none');
33470 if (this.getValue() == '') {
33471 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33474 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33476 this.markdownEl.on('click', this.toggleTextEdit, this);
33477 this.on('blur', this.toggleTextEdit, this);
33478 this.on('specialkey', this.resizeTextArea, this);
33481 toggleTextEdit : function()
33483 var sh = this.markdownEl.getHeight();
33484 this.inputEl().addClass('d-none');
33485 this.markdownEl.addClass('d-none');
33486 if (!this.editing) {
33488 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33489 this.inputEl().removeClass('d-none');
33490 this.inputEl().focus();
33491 this.editing = true;
33494 // show showdown...
33495 this.updateMarkdown();
33496 this.markdownEl.removeClass('d-none');
33497 this.editing = false;
33500 updateMarkdown : function()
33502 if (this.getValue() == '') {
33503 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33507 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33510 resizeTextArea: function () {
33513 Roo.log([sh, this.getValue().split("\n").length * 30]);
33514 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33516 setValue : function(val)
33518 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33519 if (!this.editing) {
33520 this.updateMarkdown();
33526 if (!this.editing) {
33527 this.toggleTextEdit();
33535 * Ext JS Library 1.1.1
33536 * Copyright(c) 2006-2007, Ext JS, LLC.
33538 * Originally Released Under LGPL - original licence link has changed is not relivant.
33541 * <script type="text/javascript">
33545 * @class Roo.bootstrap.PagingToolbar
33546 * @extends Roo.bootstrap.nav.Simplebar
33547 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33549 * Create a new PagingToolbar
33550 * @param {Object} config The config object
33551 * @param {Roo.data.Store} store
33553 Roo.bootstrap.PagingToolbar = function(config)
33555 // old args format still supported... - xtype is prefered..
33556 // created from xtype...
33558 this.ds = config.dataSource;
33560 if (config.store && !this.ds) {
33561 this.store= Roo.factory(config.store, Roo.data);
33562 this.ds = this.store;
33563 this.ds.xmodule = this.xmodule || false;
33566 this.toolbarItems = [];
33567 if (config.items) {
33568 this.toolbarItems = config.items;
33571 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33576 this.bind(this.ds);
33579 if (Roo.bootstrap.version == 4) {
33580 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33582 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33587 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33589 * @cfg {Roo.bootstrap.Button} buttons[]
33590 * Buttons for the toolbar
33593 * @cfg {Roo.data.Store} store
33594 * The underlying data store providing the paged data
33597 * @cfg {String/HTMLElement/Element} container
33598 * container The id or element that will contain the toolbar
33601 * @cfg {Boolean} displayInfo
33602 * True to display the displayMsg (defaults to false)
33605 * @cfg {Number} pageSize
33606 * The number of records to display per page (defaults to 20)
33610 * @cfg {String} displayMsg
33611 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33613 displayMsg : 'Displaying {0} - {1} of {2}',
33615 * @cfg {String} emptyMsg
33616 * The message to display when no records are found (defaults to "No data to display")
33618 emptyMsg : 'No data to display',
33620 * Customizable piece of the default paging text (defaults to "Page")
33623 beforePageText : "Page",
33625 * Customizable piece of the default paging text (defaults to "of %0")
33628 afterPageText : "of {0}",
33630 * Customizable piece of the default paging text (defaults to "First Page")
33633 firstText : "First Page",
33635 * Customizable piece of the default paging text (defaults to "Previous Page")
33638 prevText : "Previous Page",
33640 * Customizable piece of the default paging text (defaults to "Next Page")
33643 nextText : "Next Page",
33645 * Customizable piece of the default paging text (defaults to "Last Page")
33648 lastText : "Last Page",
33650 * Customizable piece of the default paging text (defaults to "Refresh")
33653 refreshText : "Refresh",
33657 onRender : function(ct, position)
33659 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33660 this.navgroup.parentId = this.id;
33661 this.navgroup.onRender(this.el, null);
33662 // add the buttons to the navgroup
33664 if(this.displayInfo){
33665 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33666 this.displayEl = this.el.select('.x-paging-info', true).first();
33667 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33668 // this.displayEl = navel.el.select('span',true).first();
33674 Roo.each(_this.buttons, function(e){ // this might need to use render????
33675 Roo.factory(e).render(_this.el);
33679 Roo.each(_this.toolbarItems, function(e) {
33680 _this.navgroup.addItem(e);
33684 this.first = this.navgroup.addItem({
33685 tooltip: this.firstText,
33686 cls: "prev btn-outline-secondary",
33687 html : ' <i class="fa fa-step-backward"></i>',
33689 preventDefault: true,
33690 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33693 this.prev = this.navgroup.addItem({
33694 tooltip: this.prevText,
33695 cls: "prev btn-outline-secondary",
33696 html : ' <i class="fa fa-backward"></i>',
33698 preventDefault: true,
33699 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
33701 //this.addSeparator();
33704 var field = this.navgroup.addItem( {
33706 cls : 'x-paging-position btn-outline-secondary',
33708 html : this.beforePageText +
33709 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33710 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
33713 this.field = field.el.select('input', true).first();
33714 this.field.on("keydown", this.onPagingKeydown, this);
33715 this.field.on("focus", function(){this.dom.select();});
33718 this.afterTextEl = field.el.select('.x-paging-after',true).first();
33719 //this.field.setHeight(18);
33720 //this.addSeparator();
33721 this.next = this.navgroup.addItem({
33722 tooltip: this.nextText,
33723 cls: "next btn-outline-secondary",
33724 html : ' <i class="fa fa-forward"></i>',
33726 preventDefault: true,
33727 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
33729 this.last = this.navgroup.addItem({
33730 tooltip: this.lastText,
33731 html : ' <i class="fa fa-step-forward"></i>',
33732 cls: "next btn-outline-secondary",
33734 preventDefault: true,
33735 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
33737 //this.addSeparator();
33738 this.loading = this.navgroup.addItem({
33739 tooltip: this.refreshText,
33740 cls: "btn-outline-secondary",
33741 html : ' <i class="fa fa-refresh"></i>',
33742 preventDefault: true,
33743 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33749 updateInfo : function(){
33750 if(this.displayEl){
33751 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33752 var msg = count == 0 ?
33756 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
33758 this.displayEl.update(msg);
33763 onLoad : function(ds, r, o)
33765 this.cursor = o.params && o.params.start ? o.params.start : 0;
33767 var d = this.getPageData(),
33772 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33773 this.field.dom.value = ap;
33774 this.first.setDisabled(ap == 1);
33775 this.prev.setDisabled(ap == 1);
33776 this.next.setDisabled(ap == ps);
33777 this.last.setDisabled(ap == ps);
33778 this.loading.enable();
33783 getPageData : function(){
33784 var total = this.ds.getTotalCount();
33787 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33788 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33793 onLoadError : function(proxy, o){
33794 this.loading.enable();
33795 if (this.ds.events.loadexception.listeners.length < 2) {
33796 // nothing has been assigned to loadexception except this...
33798 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33804 onPagingKeydown : function(e){
33805 var k = e.getKey();
33806 var d = this.getPageData();
33808 var v = this.field.dom.value, pageNum;
33809 if(!v || isNaN(pageNum = parseInt(v, 10))){
33810 this.field.dom.value = d.activePage;
33813 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33814 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33817 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))
33819 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33820 this.field.dom.value = pageNum;
33821 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33824 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33826 var v = this.field.dom.value, pageNum;
33827 var increment = (e.shiftKey) ? 10 : 1;
33828 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33831 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33832 this.field.dom.value = d.activePage;
33835 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33837 this.field.dom.value = parseInt(v, 10) + increment;
33838 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33839 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33846 beforeLoad : function(){
33848 this.loading.disable();
33853 onClick : function(which){
33862 ds.load({params:{start: 0, limit: this.pageSize}});
33865 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33868 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33871 var total = ds.getTotalCount();
33872 var extra = total % this.pageSize;
33873 var lastStart = extra ? (total - extra) : total-this.pageSize;
33874 ds.load({params:{start: lastStart, limit: this.pageSize}});
33877 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33883 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33884 * @param {Roo.data.Store} store The data store to unbind
33886 unbind : function(ds){
33887 ds.un("beforeload", this.beforeLoad, this);
33888 ds.un("load", this.onLoad, this);
33889 ds.un("loadexception", this.onLoadError, this);
33890 ds.un("remove", this.updateInfo, this);
33891 ds.un("add", this.updateInfo, this);
33892 this.ds = undefined;
33896 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33897 * @param {Roo.data.Store} store The data store to bind
33899 bind : function(ds){
33900 ds.on("beforeload", this.beforeLoad, this);
33901 ds.on("load", this.onLoad, this);
33902 ds.on("loadexception", this.onLoadError, this);
33903 ds.on("remove", this.updateInfo, this);
33904 ds.on("add", this.updateInfo, this);
33915 * @class Roo.bootstrap.MessageBar
33916 * @extends Roo.bootstrap.Component
33917 * Bootstrap MessageBar class
33918 * @cfg {String} html contents of the MessageBar
33919 * @cfg {String} weight (info | success | warning | danger) default info
33920 * @cfg {String} beforeClass insert the bar before the given class
33921 * @cfg {Boolean} closable (true | false) default false
33922 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33925 * Create a new Element
33926 * @param {Object} config The config object
33929 Roo.bootstrap.MessageBar = function(config){
33930 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33933 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33939 beforeClass: 'bootstrap-sticky-wrap',
33941 getAutoCreate : function(){
33945 cls: 'alert alert-dismissable alert-' + this.weight,
33950 html: this.html || ''
33956 cfg.cls += ' alert-messages-fixed';
33970 onRender : function(ct, position)
33972 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33975 var cfg = Roo.apply({}, this.getAutoCreate());
33979 cfg.cls += ' ' + this.cls;
33982 cfg.style = this.style;
33984 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33986 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33989 this.el.select('>button.close').on('click', this.hide, this);
33995 if (!this.rendered) {
34001 this.fireEvent('show', this);
34007 if (!this.rendered) {
34013 this.fireEvent('hide', this);
34016 update : function()
34018 // var e = this.el.dom.firstChild;
34020 // if(this.closable){
34021 // e = e.nextSibling;
34024 // e.data = this.html || '';
34026 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34042 * @class Roo.bootstrap.Graph
34043 * @extends Roo.bootstrap.Component
34044 * Bootstrap Graph class
34048 @cfg {String} graphtype bar | vbar | pie
34049 @cfg {number} g_x coodinator | centre x (pie)
34050 @cfg {number} g_y coodinator | centre y (pie)
34051 @cfg {number} g_r radius (pie)
34052 @cfg {number} g_height height of the chart (respected by all elements in the set)
34053 @cfg {number} g_width width of the chart (respected by all elements in the set)
34054 @cfg {Object} title The title of the chart
34057 -opts (object) options for the chart
34059 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34060 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34062 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.
34063 o stacked (boolean) whether or not to tread values as in a stacked bar chart
34065 o stretch (boolean)
34067 -opts (object) options for the pie
34070 o startAngle (number)
34071 o endAngle (number)
34075 * Create a new Input
34076 * @param {Object} config The config object
34079 Roo.bootstrap.Graph = function(config){
34080 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34086 * The img click event for the img.
34087 * @param {Roo.EventObject} e
34093 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
34104 //g_colors: this.colors,
34111 getAutoCreate : function(){
34122 onRender : function(ct,position){
34125 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34127 if (typeof(Raphael) == 'undefined') {
34128 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34132 this.raphael = Raphael(this.el.dom);
34134 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34135 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34136 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34137 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34139 r.text(160, 10, "Single Series Chart").attr(txtattr);
34140 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34141 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34142 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34144 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34145 r.barchart(330, 10, 300, 220, data1);
34146 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34147 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34150 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34151 // r.barchart(30, 30, 560, 250, xdata, {
34152 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34153 // axis : "0 0 1 1",
34154 // axisxlabels : xdata
34155 // //yvalues : cols,
34158 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34160 // this.load(null,xdata,{
34161 // axis : "0 0 1 1",
34162 // axisxlabels : xdata
34167 load : function(graphtype,xdata,opts)
34169 this.raphael.clear();
34171 graphtype = this.graphtype;
34176 var r = this.raphael,
34177 fin = function () {
34178 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34180 fout = function () {
34181 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34183 pfin = function() {
34184 this.sector.stop();
34185 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34188 this.label[0].stop();
34189 this.label[0].attr({ r: 7.5 });
34190 this.label[1].attr({ "font-weight": 800 });
34193 pfout = function() {
34194 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34197 this.label[0].animate({ r: 5 }, 500, "bounce");
34198 this.label[1].attr({ "font-weight": 400 });
34204 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34207 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34210 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
34211 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34213 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34220 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34225 setTitle: function(o)
34230 initEvents: function() {
34233 this.el.on('click', this.onClick, this);
34237 onClick : function(e)
34239 Roo.log('img onclick');
34240 this.fireEvent('click', this, e);
34246 Roo.bootstrap.dash = {};/*
34252 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34255 * @class Roo.bootstrap.dash.NumberBox
34256 * @extends Roo.bootstrap.Component
34257 * Bootstrap NumberBox class
34258 * @cfg {String} headline Box headline
34259 * @cfg {String} content Box content
34260 * @cfg {String} icon Box icon
34261 * @cfg {String} footer Footer text
34262 * @cfg {String} fhref Footer href
34265 * Create a new NumberBox
34266 * @param {Object} config The config object
34270 Roo.bootstrap.dash.NumberBox = function(config){
34271 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34275 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
34284 getAutoCreate : function(){
34288 cls : 'small-box ',
34296 cls : 'roo-headline',
34297 html : this.headline
34301 cls : 'roo-content',
34302 html : this.content
34316 cls : 'ion ' + this.icon
34325 cls : 'small-box-footer',
34326 href : this.fhref || '#',
34330 cfg.cn.push(footer);
34337 onRender : function(ct,position){
34338 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34345 setHeadline: function (value)
34347 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34350 setFooter: function (value, href)
34352 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34355 this.el.select('a.small-box-footer',true).first().attr('href', href);
34360 setContent: function (value)
34362 this.el.select('.roo-content',true).first().dom.innerHTML = value;
34365 initEvents: function()
34379 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34382 * @class Roo.bootstrap.dash.TabBox
34383 * @extends Roo.bootstrap.Component
34384 * @children Roo.bootstrap.dash.TabPane
34385 * Bootstrap TabBox class
34386 * @cfg {String} title Title of the TabBox
34387 * @cfg {String} icon Icon of the TabBox
34388 * @cfg {Boolean} showtabs (true|false) show the tabs default true
34389 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34392 * Create a new TabBox
34393 * @param {Object} config The config object
34397 Roo.bootstrap.dash.TabBox = function(config){
34398 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34403 * When a pane is added
34404 * @param {Roo.bootstrap.dash.TabPane} pane
34408 * @event activatepane
34409 * When a pane is activated
34410 * @param {Roo.bootstrap.dash.TabPane} pane
34412 "activatepane" : true
34420 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
34425 tabScrollable : false,
34427 getChildContainer : function()
34429 return this.el.select('.tab-content', true).first();
34432 getAutoCreate : function(){
34436 cls: 'pull-left header',
34444 cls: 'fa ' + this.icon
34450 cls: 'nav nav-tabs pull-right',
34456 if(this.tabScrollable){
34463 cls: 'nav nav-tabs pull-right',
34474 cls: 'nav-tabs-custom',
34479 cls: 'tab-content no-padding',
34487 initEvents : function()
34489 //Roo.log('add add pane handler');
34490 this.on('addpane', this.onAddPane, this);
34493 * Updates the box title
34494 * @param {String} html to set the title to.
34496 setTitle : function(value)
34498 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34500 onAddPane : function(pane)
34502 this.panes.push(pane);
34503 //Roo.log('addpane');
34505 // tabs are rendere left to right..
34506 if(!this.showtabs){
34510 var ctr = this.el.select('.nav-tabs', true).first();
34513 var existing = ctr.select('.nav-tab',true);
34514 var qty = existing.getCount();;
34517 var tab = ctr.createChild({
34519 cls : 'nav-tab' + (qty ? '' : ' active'),
34527 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34530 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34532 pane.el.addClass('active');
34537 onTabClick : function(ev,un,ob,pane)
34539 //Roo.log('tab - prev default');
34540 ev.preventDefault();
34543 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34544 pane.tab.addClass('active');
34545 //Roo.log(pane.title);
34546 this.getChildContainer().select('.tab-pane',true).removeClass('active');
34547 // technically we should have a deactivate event.. but maybe add later.
34548 // and it should not de-activate the selected tab...
34549 this.fireEvent('activatepane', pane);
34550 pane.el.addClass('active');
34551 pane.fireEvent('activate');
34556 getActivePane : function()
34559 Roo.each(this.panes, function(p) {
34560 if(p.el.hasClass('active')){
34581 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34583 * @class Roo.bootstrap.TabPane
34584 * @extends Roo.bootstrap.Component
34585 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
34586 * Bootstrap TabPane class
34587 * @cfg {Boolean} active (false | true) Default false
34588 * @cfg {String} title title of panel
34592 * Create a new TabPane
34593 * @param {Object} config The config object
34596 Roo.bootstrap.dash.TabPane = function(config){
34597 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34603 * When a pane is activated
34604 * @param {Roo.bootstrap.dash.TabPane} pane
34611 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
34616 // the tabBox that this is attached to.
34619 getAutoCreate : function()
34627 cfg.cls += ' active';
34632 initEvents : function()
34634 //Roo.log('trigger add pane handler');
34635 this.parent().fireEvent('addpane', this)
34639 * Updates the tab title
34640 * @param {String} html to set the title to.
34642 setTitle: function(str)
34648 this.tab.select('a', true).first().dom.innerHTML = str;
34667 * @class Roo.bootstrap.Tooltip
34668 * Bootstrap Tooltip class
34669 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34670 * to determine which dom element triggers the tooltip.
34672 * It needs to add support for additional attributes like tooltip-position
34675 * Create a new Toolti
34676 * @param {Object} config The config object
34679 Roo.bootstrap.Tooltip = function(config){
34680 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34682 this.alignment = Roo.bootstrap.Tooltip.alignment;
34684 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34685 this.alignment = config.alignment;
34690 Roo.apply(Roo.bootstrap.Tooltip, {
34692 * @function init initialize tooltip monitoring.
34696 currentTip : false,
34697 currentRegion : false,
34703 Roo.get(document).on('mouseover', this.enter ,this);
34704 Roo.get(document).on('mouseout', this.leave, this);
34707 this.currentTip = new Roo.bootstrap.Tooltip();
34710 enter : function(ev)
34712 var dom = ev.getTarget();
34714 //Roo.log(['enter',dom]);
34715 var el = Roo.fly(dom);
34716 if (this.currentEl) {
34718 //Roo.log(this.currentEl);
34719 //Roo.log(this.currentEl.contains(dom));
34720 if (this.currentEl == el) {
34723 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34729 if (this.currentTip.el) {
34730 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34734 if(!el || el.dom == document){
34740 if (!el.attr('tooltip')) {
34741 pel = el.findParent("[tooltip]");
34743 bindEl = Roo.get(pel);
34749 // you can not look for children, as if el is the body.. then everythign is the child..
34750 if (!pel && !el.attr('tooltip')) { //
34751 if (!el.select("[tooltip]").elements.length) {
34754 // is the mouse over this child...?
34755 bindEl = el.select("[tooltip]").first();
34756 var xy = ev.getXY();
34757 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34758 //Roo.log("not in region.");
34761 //Roo.log("child element over..");
34764 this.currentEl = el;
34765 this.currentTip.bind(bindEl);
34766 this.currentRegion = Roo.lib.Region.getRegion(dom);
34767 this.currentTip.enter();
34770 leave : function(ev)
34772 var dom = ev.getTarget();
34773 //Roo.log(['leave',dom]);
34774 if (!this.currentEl) {
34779 if (dom != this.currentEl.dom) {
34782 var xy = ev.getXY();
34783 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
34786 // only activate leave if mouse cursor is outside... bounding box..
34791 if (this.currentTip) {
34792 this.currentTip.leave();
34794 //Roo.log('clear currentEl');
34795 this.currentEl = false;
34800 'left' : ['r-l', [-2,0], 'right'],
34801 'right' : ['l-r', [2,0], 'left'],
34802 'bottom' : ['t-b', [0,2], 'top'],
34803 'top' : [ 'b-t', [0,-2], 'bottom']
34809 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
34814 delay : null, // can be { show : 300 , hide: 500}
34818 hoverState : null, //???
34820 placement : 'bottom',
34824 getAutoCreate : function(){
34831 cls : 'tooltip-arrow arrow'
34834 cls : 'tooltip-inner'
34841 bind : function(el)
34846 initEvents : function()
34848 this.arrowEl = this.el.select('.arrow', true).first();
34849 this.innerEl = this.el.select('.tooltip-inner', true).first();
34852 enter : function () {
34854 if (this.timeout != null) {
34855 clearTimeout(this.timeout);
34858 this.hoverState = 'in';
34859 //Roo.log("enter - show");
34860 if (!this.delay || !this.delay.show) {
34865 this.timeout = setTimeout(function () {
34866 if (_t.hoverState == 'in') {
34869 }, this.delay.show);
34873 clearTimeout(this.timeout);
34875 this.hoverState = 'out';
34876 if (!this.delay || !this.delay.hide) {
34882 this.timeout = setTimeout(function () {
34883 //Roo.log("leave - timeout");
34885 if (_t.hoverState == 'out') {
34887 Roo.bootstrap.Tooltip.currentEl = false;
34892 show : function (msg)
34895 this.render(document.body);
34898 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34900 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34902 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34904 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34905 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34907 if(this.bindEl.attr('tooltip-class')) {
34908 this.el.addClass(this.bindEl.attr('tooltip-class'));
34911 var placement = typeof this.placement == 'function' ?
34912 this.placement.call(this, this.el, on_el) :
34915 if(this.bindEl.attr('tooltip-placement')) {
34916 placement = this.bindEl.attr('tooltip-placement');
34919 var autoToken = /\s?auto?\s?/i;
34920 var autoPlace = autoToken.test(placement);
34922 placement = placement.replace(autoToken, '') || 'top';
34926 //this.el.setXY([0,0]);
34928 //this.el.dom.style.display='block';
34930 //this.el.appendTo(on_el);
34932 var p = this.getPosition();
34933 var box = this.el.getBox();
34939 var align = this.alignment[placement];
34941 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34943 if(placement == 'top' || placement == 'bottom'){
34945 placement = 'right';
34948 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34949 placement = 'left';
34952 var scroll = Roo.select('body', true).first().getScroll();
34954 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34958 align = this.alignment[placement];
34960 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34964 var elems = document.getElementsByTagName('div');
34965 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34966 for (var i = 0; i < elems.length; i++) {
34967 var zindex = Number.parseInt(
34968 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34971 if (zindex > highest) {
34978 this.el.dom.style.zIndex = highest;
34980 this.el.alignTo(this.bindEl, align[0],align[1]);
34981 //var arrow = this.el.select('.arrow',true).first();
34982 //arrow.set(align[2],
34984 this.el.addClass(placement);
34985 this.el.addClass("bs-tooltip-"+ placement);
34987 this.el.addClass('in fade show');
34989 this.hoverState = null;
34991 if (this.el.hasClass('fade')) {
35006 //this.el.setXY([0,0]);
35007 if(this.bindEl.attr('tooltip-class')) {
35008 this.el.removeClass(this.bindEl.attr('tooltip-class'));
35010 this.el.removeClass(['show', 'in']);
35026 * @class Roo.bootstrap.LocationPicker
35027 * @extends Roo.bootstrap.Component
35028 * Bootstrap LocationPicker class
35029 * @cfg {Number} latitude Position when init default 0
35030 * @cfg {Number} longitude Position when init default 0
35031 * @cfg {Number} zoom default 15
35032 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35033 * @cfg {Boolean} mapTypeControl default false
35034 * @cfg {Boolean} disableDoubleClickZoom default false
35035 * @cfg {Boolean} scrollwheel default true
35036 * @cfg {Boolean} streetViewControl default false
35037 * @cfg {Number} radius default 0
35038 * @cfg {String} locationName
35039 * @cfg {Boolean} draggable default true
35040 * @cfg {Boolean} enableAutocomplete default false
35041 * @cfg {Boolean} enableReverseGeocode default true
35042 * @cfg {String} markerTitle
35045 * Create a new LocationPicker
35046 * @param {Object} config The config object
35050 Roo.bootstrap.LocationPicker = function(config){
35052 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35057 * Fires when the picker initialized.
35058 * @param {Roo.bootstrap.LocationPicker} this
35059 * @param {Google Location} location
35063 * @event positionchanged
35064 * Fires when the picker position changed.
35065 * @param {Roo.bootstrap.LocationPicker} this
35066 * @param {Google Location} location
35068 positionchanged : true,
35071 * Fires when the map resize.
35072 * @param {Roo.bootstrap.LocationPicker} this
35077 * Fires when the map show.
35078 * @param {Roo.bootstrap.LocationPicker} this
35083 * Fires when the map hide.
35084 * @param {Roo.bootstrap.LocationPicker} this
35089 * Fires when click the map.
35090 * @param {Roo.bootstrap.LocationPicker} this
35091 * @param {Map event} e
35095 * @event mapRightClick
35096 * Fires when right click the map.
35097 * @param {Roo.bootstrap.LocationPicker} this
35098 * @param {Map event} e
35100 mapRightClick : true,
35102 * @event markerClick
35103 * Fires when click the marker.
35104 * @param {Roo.bootstrap.LocationPicker} this
35105 * @param {Map event} e
35107 markerClick : true,
35109 * @event markerRightClick
35110 * Fires when right click the marker.
35111 * @param {Roo.bootstrap.LocationPicker} this
35112 * @param {Map event} e
35114 markerRightClick : true,
35116 * @event OverlayViewDraw
35117 * Fires when OverlayView Draw
35118 * @param {Roo.bootstrap.LocationPicker} this
35120 OverlayViewDraw : true,
35122 * @event OverlayViewOnAdd
35123 * Fires when OverlayView Draw
35124 * @param {Roo.bootstrap.LocationPicker} this
35126 OverlayViewOnAdd : true,
35128 * @event OverlayViewOnRemove
35129 * Fires when OverlayView Draw
35130 * @param {Roo.bootstrap.LocationPicker} this
35132 OverlayViewOnRemove : true,
35134 * @event OverlayViewShow
35135 * Fires when OverlayView Draw
35136 * @param {Roo.bootstrap.LocationPicker} this
35137 * @param {Pixel} cpx
35139 OverlayViewShow : true,
35141 * @event OverlayViewHide
35142 * Fires when OverlayView Draw
35143 * @param {Roo.bootstrap.LocationPicker} this
35145 OverlayViewHide : true,
35147 * @event loadexception
35148 * Fires when load google lib failed.
35149 * @param {Roo.bootstrap.LocationPicker} this
35151 loadexception : true
35156 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
35158 gMapContext: false,
35164 mapTypeControl: false,
35165 disableDoubleClickZoom: false,
35167 streetViewControl: false,
35171 enableAutocomplete: false,
35172 enableReverseGeocode: true,
35175 getAutoCreate: function()
35180 cls: 'roo-location-picker'
35186 initEvents: function(ct, position)
35188 if(!this.el.getWidth() || this.isApplied()){
35192 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35197 initial: function()
35199 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35200 this.fireEvent('loadexception', this);
35204 if(!this.mapTypeId){
35205 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35208 this.gMapContext = this.GMapContext();
35210 this.initOverlayView();
35212 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35216 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35217 _this.setPosition(_this.gMapContext.marker.position);
35220 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35221 _this.fireEvent('mapClick', this, event);
35225 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35226 _this.fireEvent('mapRightClick', this, event);
35230 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35231 _this.fireEvent('markerClick', this, event);
35235 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35236 _this.fireEvent('markerRightClick', this, event);
35240 this.setPosition(this.gMapContext.location);
35242 this.fireEvent('initial', this, this.gMapContext.location);
35245 initOverlayView: function()
35249 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35253 _this.fireEvent('OverlayViewDraw', _this);
35258 _this.fireEvent('OverlayViewOnAdd', _this);
35261 onRemove: function()
35263 _this.fireEvent('OverlayViewOnRemove', _this);
35266 show: function(cpx)
35268 _this.fireEvent('OverlayViewShow', _this, cpx);
35273 _this.fireEvent('OverlayViewHide', _this);
35279 fromLatLngToContainerPixel: function(event)
35281 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35284 isApplied: function()
35286 return this.getGmapContext() == false ? false : true;
35289 getGmapContext: function()
35291 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35294 GMapContext: function()
35296 var position = new google.maps.LatLng(this.latitude, this.longitude);
35298 var _map = new google.maps.Map(this.el.dom, {
35301 mapTypeId: this.mapTypeId,
35302 mapTypeControl: this.mapTypeControl,
35303 disableDoubleClickZoom: this.disableDoubleClickZoom,
35304 scrollwheel: this.scrollwheel,
35305 streetViewControl: this.streetViewControl,
35306 locationName: this.locationName,
35307 draggable: this.draggable,
35308 enableAutocomplete: this.enableAutocomplete,
35309 enableReverseGeocode: this.enableReverseGeocode
35312 var _marker = new google.maps.Marker({
35313 position: position,
35315 title: this.markerTitle,
35316 draggable: this.draggable
35323 location: position,
35324 radius: this.radius,
35325 locationName: this.locationName,
35326 addressComponents: {
35327 formatted_address: null,
35328 addressLine1: null,
35329 addressLine2: null,
35331 streetNumber: null,
35335 stateOrProvince: null
35338 domContainer: this.el.dom,
35339 geodecoder: new google.maps.Geocoder()
35343 drawCircle: function(center, radius, options)
35345 if (this.gMapContext.circle != null) {
35346 this.gMapContext.circle.setMap(null);
35350 options = Roo.apply({}, options, {
35351 strokeColor: "#0000FF",
35352 strokeOpacity: .35,
35354 fillColor: "#0000FF",
35358 options.map = this.gMapContext.map;
35359 options.radius = radius;
35360 options.center = center;
35361 this.gMapContext.circle = new google.maps.Circle(options);
35362 return this.gMapContext.circle;
35368 setPosition: function(location)
35370 this.gMapContext.location = location;
35371 this.gMapContext.marker.setPosition(location);
35372 this.gMapContext.map.panTo(location);
35373 this.drawCircle(location, this.gMapContext.radius, {});
35377 if (this.gMapContext.settings.enableReverseGeocode) {
35378 this.gMapContext.geodecoder.geocode({
35379 latLng: this.gMapContext.location
35380 }, function(results, status) {
35382 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35383 _this.gMapContext.locationName = results[0].formatted_address;
35384 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35386 _this.fireEvent('positionchanged', this, location);
35393 this.fireEvent('positionchanged', this, location);
35398 google.maps.event.trigger(this.gMapContext.map, "resize");
35400 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35402 this.fireEvent('resize', this);
35405 setPositionByLatLng: function(latitude, longitude)
35407 this.setPosition(new google.maps.LatLng(latitude, longitude));
35410 getCurrentPosition: function()
35413 latitude: this.gMapContext.location.lat(),
35414 longitude: this.gMapContext.location.lng()
35418 getAddressName: function()
35420 return this.gMapContext.locationName;
35423 getAddressComponents: function()
35425 return this.gMapContext.addressComponents;
35428 address_component_from_google_geocode: function(address_components)
35432 for (var i = 0; i < address_components.length; i++) {
35433 var component = address_components[i];
35434 if (component.types.indexOf("postal_code") >= 0) {
35435 result.postalCode = component.short_name;
35436 } else if (component.types.indexOf("street_number") >= 0) {
35437 result.streetNumber = component.short_name;
35438 } else if (component.types.indexOf("route") >= 0) {
35439 result.streetName = component.short_name;
35440 } else if (component.types.indexOf("neighborhood") >= 0) {
35441 result.city = component.short_name;
35442 } else if (component.types.indexOf("locality") >= 0) {
35443 result.city = component.short_name;
35444 } else if (component.types.indexOf("sublocality") >= 0) {
35445 result.district = component.short_name;
35446 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35447 result.stateOrProvince = component.short_name;
35448 } else if (component.types.indexOf("country") >= 0) {
35449 result.country = component.short_name;
35453 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35454 result.addressLine2 = "";
35458 setZoomLevel: function(zoom)
35460 this.gMapContext.map.setZoom(zoom);
35473 this.fireEvent('show', this);
35484 this.fireEvent('hide', this);
35489 Roo.apply(Roo.bootstrap.LocationPicker, {
35491 OverlayView : function(map, options)
35493 options = options || {};
35500 * @class Roo.bootstrap.Alert
35501 * @extends Roo.bootstrap.Component
35502 * Bootstrap Alert class - shows an alert area box
35504 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35505 Enter a valid email address
35508 * @cfg {String} title The title of alert
35509 * @cfg {String} html The content of alert
35510 * @cfg {String} weight (success|info|warning|danger) Weight of the message
35511 * @cfg {String} fa font-awesomeicon
35512 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35513 * @cfg {Boolean} close true to show a x closer
35517 * Create a new alert
35518 * @param {Object} config The config object
35522 Roo.bootstrap.Alert = function(config){
35523 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35527 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
35533 faicon: false, // BC
35537 getAutoCreate : function()
35549 style : this.close ? '' : 'display:none'
35553 cls : 'roo-alert-icon'
35558 cls : 'roo-alert-title',
35563 cls : 'roo-alert-text',
35570 cfg.cn[0].cls += ' fa ' + this.faicon;
35573 cfg.cn[0].cls += ' fa ' + this.fa;
35577 cfg.cls += ' alert-' + this.weight;
35583 initEvents: function()
35585 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35586 this.titleEl = this.el.select('.roo-alert-title',true).first();
35587 this.iconEl = this.el.select('.roo-alert-icon',true).first();
35588 this.htmlEl = this.el.select('.roo-alert-text',true).first();
35589 if (this.seconds > 0) {
35590 this.hide.defer(this.seconds, this);
35594 * Set the Title Message HTML
35595 * @param {String} html
35597 setTitle : function(str)
35599 this.titleEl.dom.innerHTML = str;
35603 * Set the Body Message HTML
35604 * @param {String} html
35606 setHtml : function(str)
35608 this.htmlEl.dom.innerHTML = str;
35611 * Set the Weight of the alert
35612 * @param {String} (success|info|warning|danger) weight
35615 setWeight : function(weight)
35618 this.el.removeClass('alert-' + this.weight);
35621 this.weight = weight;
35623 this.el.addClass('alert-' + this.weight);
35626 * Set the Icon of the alert
35627 * @param {String} see fontawsome names (name without the 'fa-' bit)
35629 setIcon : function(icon)
35632 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35635 this.faicon = icon;
35637 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35662 * @class Roo.bootstrap.UploadCropbox
35663 * @extends Roo.bootstrap.Component
35664 * Bootstrap UploadCropbox class
35665 * @cfg {String} emptyText show when image has been loaded
35666 * @cfg {String} rotateNotify show when image too small to rotate
35667 * @cfg {Number} errorTimeout default 3000
35668 * @cfg {Number} minWidth default 300
35669 * @cfg {Number} minHeight default 300
35670 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35671 * @cfg {Boolean} isDocument (true|false) default false
35672 * @cfg {String} url action url
35673 * @cfg {String} paramName default 'imageUpload'
35674 * @cfg {String} method default POST
35675 * @cfg {Boolean} loadMask (true|false) default true
35676 * @cfg {Boolean} loadingText default 'Loading...'
35679 * Create a new UploadCropbox
35680 * @param {Object} config The config object
35683 Roo.bootstrap.UploadCropbox = function(config){
35684 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35688 * @event beforeselectfile
35689 * Fire before select file
35690 * @param {Roo.bootstrap.UploadCropbox} this
35692 "beforeselectfile" : true,
35695 * Fire after initEvent
35696 * @param {Roo.bootstrap.UploadCropbox} this
35701 * Fire after initEvent
35702 * @param {Roo.bootstrap.UploadCropbox} this
35703 * @param {String} data
35708 * Fire when preparing the file data
35709 * @param {Roo.bootstrap.UploadCropbox} this
35710 * @param {Object} file
35715 * Fire when get exception
35716 * @param {Roo.bootstrap.UploadCropbox} this
35717 * @param {XMLHttpRequest} xhr
35719 "exception" : true,
35721 * @event beforeloadcanvas
35722 * Fire before load the canvas
35723 * @param {Roo.bootstrap.UploadCropbox} this
35724 * @param {String} src
35726 "beforeloadcanvas" : true,
35729 * Fire when trash image
35730 * @param {Roo.bootstrap.UploadCropbox} this
35735 * Fire when download the image
35736 * @param {Roo.bootstrap.UploadCropbox} this
35740 * @event footerbuttonclick
35741 * Fire when footerbuttonclick
35742 * @param {Roo.bootstrap.UploadCropbox} this
35743 * @param {String} type
35745 "footerbuttonclick" : true,
35749 * @param {Roo.bootstrap.UploadCropbox} this
35754 * Fire when rotate the image
35755 * @param {Roo.bootstrap.UploadCropbox} this
35756 * @param {String} pos
35761 * Fire when inspect the file
35762 * @param {Roo.bootstrap.UploadCropbox} this
35763 * @param {Object} file
35768 * Fire when xhr upload the file
35769 * @param {Roo.bootstrap.UploadCropbox} this
35770 * @param {Object} data
35775 * Fire when arrange the file data
35776 * @param {Roo.bootstrap.UploadCropbox} this
35777 * @param {Object} formData
35782 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35785 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
35787 emptyText : 'Click to upload image',
35788 rotateNotify : 'Image is too small to rotate',
35789 errorTimeout : 3000,
35803 cropType : 'image/jpeg',
35805 canvasLoaded : false,
35806 isDocument : false,
35808 paramName : 'imageUpload',
35810 loadingText : 'Loading...',
35813 getAutoCreate : function()
35817 cls : 'roo-upload-cropbox',
35821 cls : 'roo-upload-cropbox-selector',
35826 cls : 'roo-upload-cropbox-body',
35827 style : 'cursor:pointer',
35831 cls : 'roo-upload-cropbox-preview'
35835 cls : 'roo-upload-cropbox-thumb'
35839 cls : 'roo-upload-cropbox-empty-notify',
35840 html : this.emptyText
35844 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35845 html : this.rotateNotify
35851 cls : 'roo-upload-cropbox-footer',
35854 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35864 onRender : function(ct, position)
35866 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35868 if (this.buttons.length) {
35870 Roo.each(this.buttons, function(bb) {
35872 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35874 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35880 this.maskEl = this.el;
35884 initEvents : function()
35886 this.urlAPI = (window.createObjectURL && window) ||
35887 (window.URL && URL.revokeObjectURL && URL) ||
35888 (window.webkitURL && webkitURL);
35890 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35891 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35893 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35894 this.selectorEl.hide();
35896 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35897 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35899 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35900 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35901 this.thumbEl.hide();
35903 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35904 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35906 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35907 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35908 this.errorEl.hide();
35910 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35911 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35912 this.footerEl.hide();
35914 this.setThumbBoxSize();
35920 this.fireEvent('initial', this);
35927 window.addEventListener("resize", function() { _this.resize(); } );
35929 this.bodyEl.on('click', this.beforeSelectFile, this);
35932 this.bodyEl.on('touchstart', this.onTouchStart, this);
35933 this.bodyEl.on('touchmove', this.onTouchMove, this);
35934 this.bodyEl.on('touchend', this.onTouchEnd, this);
35938 this.bodyEl.on('mousedown', this.onMouseDown, this);
35939 this.bodyEl.on('mousemove', this.onMouseMove, this);
35940 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35941 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35942 Roo.get(document).on('mouseup', this.onMouseUp, this);
35945 this.selectorEl.on('change', this.onFileSelected, this);
35951 this.baseScale = 1;
35953 this.baseRotate = 1;
35954 this.dragable = false;
35955 this.pinching = false;
35958 this.cropData = false;
35959 this.notifyEl.dom.innerHTML = this.emptyText;
35961 this.selectorEl.dom.value = '';
35965 resize : function()
35967 if(this.fireEvent('resize', this) != false){
35968 this.setThumbBoxPosition();
35969 this.setCanvasPosition();
35973 onFooterButtonClick : function(e, el, o, type)
35976 case 'rotate-left' :
35977 this.onRotateLeft(e);
35979 case 'rotate-right' :
35980 this.onRotateRight(e);
35983 this.beforeSelectFile(e);
35998 this.fireEvent('footerbuttonclick', this, type);
36001 beforeSelectFile : function(e)
36003 e.preventDefault();
36005 if(this.fireEvent('beforeselectfile', this) != false){
36006 this.selectorEl.dom.click();
36010 onFileSelected : function(e)
36012 e.preventDefault();
36014 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36018 var file = this.selectorEl.dom.files[0];
36020 if(this.fireEvent('inspect', this, file) != false){
36021 this.prepare(file);
36026 trash : function(e)
36028 this.fireEvent('trash', this);
36031 download : function(e)
36033 this.fireEvent('download', this);
36036 loadCanvas : function(src)
36038 if(this.fireEvent('beforeloadcanvas', this, src) != false){
36042 this.imageEl = document.createElement('img');
36046 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36048 this.imageEl.src = src;
36052 onLoadCanvas : function()
36054 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36055 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36057 this.bodyEl.un('click', this.beforeSelectFile, this);
36059 this.notifyEl.hide();
36060 this.thumbEl.show();
36061 this.footerEl.show();
36063 this.baseRotateLevel();
36065 if(this.isDocument){
36066 this.setThumbBoxSize();
36069 this.setThumbBoxPosition();
36071 this.baseScaleLevel();
36077 this.canvasLoaded = true;
36080 this.maskEl.unmask();
36085 setCanvasPosition : function()
36087 if(!this.canvasEl){
36091 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36092 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36094 this.previewEl.setLeft(pw);
36095 this.previewEl.setTop(ph);
36099 onMouseDown : function(e)
36103 this.dragable = true;
36104 this.pinching = false;
36106 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36107 this.dragable = false;
36111 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36112 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36116 onMouseMove : function(e)
36120 if(!this.canvasLoaded){
36124 if (!this.dragable){
36128 var minX = Math.ceil(this.thumbEl.getLeft(true));
36129 var minY = Math.ceil(this.thumbEl.getTop(true));
36131 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36132 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36134 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36135 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36137 x = x - this.mouseX;
36138 y = y - this.mouseY;
36140 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36141 var bgY = Math.ceil(y + this.previewEl.getTop(true));
36143 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36144 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36146 this.previewEl.setLeft(bgX);
36147 this.previewEl.setTop(bgY);
36149 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36150 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36153 onMouseUp : function(e)
36157 this.dragable = false;
36160 onMouseWheel : function(e)
36164 this.startScale = this.scale;
36166 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36168 if(!this.zoomable()){
36169 this.scale = this.startScale;
36178 zoomable : function()
36180 var minScale = this.thumbEl.getWidth() / this.minWidth;
36182 if(this.minWidth < this.minHeight){
36183 minScale = this.thumbEl.getHeight() / this.minHeight;
36186 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36187 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36191 (this.rotate == 0 || this.rotate == 180) &&
36193 width > this.imageEl.OriginWidth ||
36194 height > this.imageEl.OriginHeight ||
36195 (width < this.minWidth && height < this.minHeight)
36203 (this.rotate == 90 || this.rotate == 270) &&
36205 width > this.imageEl.OriginWidth ||
36206 height > this.imageEl.OriginHeight ||
36207 (width < this.minHeight && height < this.minWidth)
36214 !this.isDocument &&
36215 (this.rotate == 0 || this.rotate == 180) &&
36217 width < this.minWidth ||
36218 width > this.imageEl.OriginWidth ||
36219 height < this.minHeight ||
36220 height > this.imageEl.OriginHeight
36227 !this.isDocument &&
36228 (this.rotate == 90 || this.rotate == 270) &&
36230 width < this.minHeight ||
36231 width > this.imageEl.OriginWidth ||
36232 height < this.minWidth ||
36233 height > this.imageEl.OriginHeight
36243 onRotateLeft : function(e)
36245 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36247 var minScale = this.thumbEl.getWidth() / this.minWidth;
36249 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36250 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36252 this.startScale = this.scale;
36254 while (this.getScaleLevel() < minScale){
36256 this.scale = this.scale + 1;
36258 if(!this.zoomable()){
36263 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36264 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36269 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36276 this.scale = this.startScale;
36278 this.onRotateFail();
36283 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36285 if(this.isDocument){
36286 this.setThumbBoxSize();
36287 this.setThumbBoxPosition();
36288 this.setCanvasPosition();
36293 this.fireEvent('rotate', this, 'left');
36297 onRotateRight : function(e)
36299 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36301 var minScale = this.thumbEl.getWidth() / this.minWidth;
36303 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36304 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36306 this.startScale = this.scale;
36308 while (this.getScaleLevel() < minScale){
36310 this.scale = this.scale + 1;
36312 if(!this.zoomable()){
36317 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36318 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36323 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36330 this.scale = this.startScale;
36332 this.onRotateFail();
36337 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36339 if(this.isDocument){
36340 this.setThumbBoxSize();
36341 this.setThumbBoxPosition();
36342 this.setCanvasPosition();
36347 this.fireEvent('rotate', this, 'right');
36350 onRotateFail : function()
36352 this.errorEl.show(true);
36356 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36361 this.previewEl.dom.innerHTML = '';
36363 var canvasEl = document.createElement("canvas");
36365 var contextEl = canvasEl.getContext("2d");
36367 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36368 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36369 var center = this.imageEl.OriginWidth / 2;
36371 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36372 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36373 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36374 center = this.imageEl.OriginHeight / 2;
36377 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36379 contextEl.translate(center, center);
36380 contextEl.rotate(this.rotate * Math.PI / 180);
36382 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36384 this.canvasEl = document.createElement("canvas");
36386 this.contextEl = this.canvasEl.getContext("2d");
36388 switch (this.rotate) {
36391 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36392 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
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.OriginHeight * this.getScaleLevel();
36400 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36402 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36403 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);
36407 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36412 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36413 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36415 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36416 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);
36420 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);
36425 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36426 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36428 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36429 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36433 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);
36440 this.previewEl.appendChild(this.canvasEl);
36442 this.setCanvasPosition();
36447 if(!this.canvasLoaded){
36451 var imageCanvas = document.createElement("canvas");
36453 var imageContext = imageCanvas.getContext("2d");
36455 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36456 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36458 var center = imageCanvas.width / 2;
36460 imageContext.translate(center, center);
36462 imageContext.rotate(this.rotate * Math.PI / 180);
36464 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36466 var canvas = document.createElement("canvas");
36468 var context = canvas.getContext("2d");
36470 canvas.width = this.minWidth;
36471 canvas.height = this.minHeight;
36473 switch (this.rotate) {
36476 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36477 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36479 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36480 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36482 var targetWidth = this.minWidth - 2 * x;
36483 var targetHeight = this.minHeight - 2 * y;
36487 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36488 scale = targetWidth / width;
36491 if(x > 0 && y == 0){
36492 scale = targetHeight / height;
36495 if(x > 0 && y > 0){
36496 scale = targetWidth / width;
36498 if(width < height){
36499 scale = targetHeight / height;
36503 context.scale(scale, scale);
36505 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36506 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36508 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36509 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36511 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36516 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36517 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36519 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36520 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36522 var targetWidth = this.minWidth - 2 * x;
36523 var targetHeight = this.minHeight - 2 * y;
36527 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36528 scale = targetWidth / width;
36531 if(x > 0 && y == 0){
36532 scale = targetHeight / height;
36535 if(x > 0 && y > 0){
36536 scale = targetWidth / width;
36538 if(width < height){
36539 scale = targetHeight / height;
36543 context.scale(scale, scale);
36545 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36546 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36548 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36549 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36551 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36553 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36558 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36559 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36561 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36562 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36564 var targetWidth = this.minWidth - 2 * x;
36565 var targetHeight = this.minHeight - 2 * y;
36569 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36570 scale = targetWidth / width;
36573 if(x > 0 && y == 0){
36574 scale = targetHeight / height;
36577 if(x > 0 && y > 0){
36578 scale = targetWidth / width;
36580 if(width < height){
36581 scale = targetHeight / height;
36585 context.scale(scale, scale);
36587 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36588 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36590 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36591 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36593 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36594 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36596 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36601 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36602 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36604 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36605 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36607 var targetWidth = this.minWidth - 2 * x;
36608 var targetHeight = this.minHeight - 2 * y;
36612 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36613 scale = targetWidth / width;
36616 if(x > 0 && y == 0){
36617 scale = targetHeight / height;
36620 if(x > 0 && y > 0){
36621 scale = targetWidth / width;
36623 if(width < height){
36624 scale = targetHeight / height;
36628 context.scale(scale, scale);
36630 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36631 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36633 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36634 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36636 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36638 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36645 this.cropData = canvas.toDataURL(this.cropType);
36647 if(this.fireEvent('crop', this, this.cropData) !== false){
36648 this.process(this.file, this.cropData);
36655 setThumbBoxSize : function()
36659 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36660 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36661 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36663 this.minWidth = width;
36664 this.minHeight = height;
36666 if(this.rotate == 90 || this.rotate == 270){
36667 this.minWidth = height;
36668 this.minHeight = width;
36673 width = Math.ceil(this.minWidth * height / this.minHeight);
36675 if(this.minWidth > this.minHeight){
36677 height = Math.ceil(this.minHeight * width / this.minWidth);
36680 this.thumbEl.setStyle({
36681 width : width + 'px',
36682 height : height + 'px'
36689 setThumbBoxPosition : function()
36691 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36692 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36694 this.thumbEl.setLeft(x);
36695 this.thumbEl.setTop(y);
36699 baseRotateLevel : function()
36701 this.baseRotate = 1;
36704 typeof(this.exif) != 'undefined' &&
36705 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36706 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36708 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36711 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36715 baseScaleLevel : function()
36719 if(this.isDocument){
36721 if(this.baseRotate == 6 || this.baseRotate == 8){
36723 height = this.thumbEl.getHeight();
36724 this.baseScale = height / this.imageEl.OriginWidth;
36726 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36727 width = this.thumbEl.getWidth();
36728 this.baseScale = width / this.imageEl.OriginHeight;
36734 height = this.thumbEl.getHeight();
36735 this.baseScale = height / this.imageEl.OriginHeight;
36737 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36738 width = this.thumbEl.getWidth();
36739 this.baseScale = width / this.imageEl.OriginWidth;
36745 if(this.baseRotate == 6 || this.baseRotate == 8){
36747 width = this.thumbEl.getHeight();
36748 this.baseScale = width / this.imageEl.OriginHeight;
36750 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36751 height = this.thumbEl.getWidth();
36752 this.baseScale = height / this.imageEl.OriginHeight;
36755 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36756 height = this.thumbEl.getWidth();
36757 this.baseScale = height / this.imageEl.OriginHeight;
36759 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36760 width = this.thumbEl.getHeight();
36761 this.baseScale = width / this.imageEl.OriginWidth;
36768 width = this.thumbEl.getWidth();
36769 this.baseScale = width / this.imageEl.OriginWidth;
36771 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36772 height = this.thumbEl.getHeight();
36773 this.baseScale = height / this.imageEl.OriginHeight;
36776 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36778 height = this.thumbEl.getHeight();
36779 this.baseScale = height / this.imageEl.OriginHeight;
36781 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36782 width = this.thumbEl.getWidth();
36783 this.baseScale = width / this.imageEl.OriginWidth;
36791 getScaleLevel : function()
36793 return this.baseScale * Math.pow(1.1, this.scale);
36796 onTouchStart : function(e)
36798 if(!this.canvasLoaded){
36799 this.beforeSelectFile(e);
36803 var touches = e.browserEvent.touches;
36809 if(touches.length == 1){
36810 this.onMouseDown(e);
36814 if(touches.length != 2){
36820 for(var i = 0, finger; finger = touches[i]; i++){
36821 coords.push(finger.pageX, finger.pageY);
36824 var x = Math.pow(coords[0] - coords[2], 2);
36825 var y = Math.pow(coords[1] - coords[3], 2);
36827 this.startDistance = Math.sqrt(x + y);
36829 this.startScale = this.scale;
36831 this.pinching = true;
36832 this.dragable = false;
36836 onTouchMove : function(e)
36838 if(!this.pinching && !this.dragable){
36842 var touches = e.browserEvent.touches;
36849 this.onMouseMove(e);
36855 for(var i = 0, finger; finger = touches[i]; i++){
36856 coords.push(finger.pageX, finger.pageY);
36859 var x = Math.pow(coords[0] - coords[2], 2);
36860 var y = Math.pow(coords[1] - coords[3], 2);
36862 this.endDistance = Math.sqrt(x + y);
36864 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36866 if(!this.zoomable()){
36867 this.scale = this.startScale;
36875 onTouchEnd : function(e)
36877 this.pinching = false;
36878 this.dragable = false;
36882 process : function(file, crop)
36885 this.maskEl.mask(this.loadingText);
36888 this.xhr = new XMLHttpRequest();
36890 file.xhr = this.xhr;
36892 this.xhr.open(this.method, this.url, true);
36895 "Accept": "application/json",
36896 "Cache-Control": "no-cache",
36897 "X-Requested-With": "XMLHttpRequest"
36900 for (var headerName in headers) {
36901 var headerValue = headers[headerName];
36903 this.xhr.setRequestHeader(headerName, headerValue);
36909 this.xhr.onload = function()
36911 _this.xhrOnLoad(_this.xhr);
36914 this.xhr.onerror = function()
36916 _this.xhrOnError(_this.xhr);
36919 var formData = new FormData();
36921 formData.append('returnHTML', 'NO');
36924 formData.append('crop', crop);
36927 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36928 formData.append(this.paramName, file, file.name);
36931 if(typeof(file.filename) != 'undefined'){
36932 formData.append('filename', file.filename);
36935 if(typeof(file.mimetype) != 'undefined'){
36936 formData.append('mimetype', file.mimetype);
36939 if(this.fireEvent('arrange', this, formData) != false){
36940 this.xhr.send(formData);
36944 xhrOnLoad : function(xhr)
36947 this.maskEl.unmask();
36950 if (xhr.readyState !== 4) {
36951 this.fireEvent('exception', this, xhr);
36955 var response = Roo.decode(xhr.responseText);
36957 if(!response.success){
36958 this.fireEvent('exception', this, xhr);
36962 var response = Roo.decode(xhr.responseText);
36964 this.fireEvent('upload', this, response);
36968 xhrOnError : function()
36971 this.maskEl.unmask();
36974 Roo.log('xhr on error');
36976 var response = Roo.decode(xhr.responseText);
36982 prepare : function(file)
36985 this.maskEl.mask(this.loadingText);
36991 if(typeof(file) === 'string'){
36992 this.loadCanvas(file);
36996 if(!file || !this.urlAPI){
37001 this.cropType = file.type;
37005 if(this.fireEvent('prepare', this, this.file) != false){
37007 var reader = new FileReader();
37009 reader.onload = function (e) {
37010 if (e.target.error) {
37011 Roo.log(e.target.error);
37015 var buffer = e.target.result,
37016 dataView = new DataView(buffer),
37018 maxOffset = dataView.byteLength - 4,
37022 if (dataView.getUint16(0) === 0xffd8) {
37023 while (offset < maxOffset) {
37024 markerBytes = dataView.getUint16(offset);
37026 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37027 markerLength = dataView.getUint16(offset + 2) + 2;
37028 if (offset + markerLength > dataView.byteLength) {
37029 Roo.log('Invalid meta data: Invalid segment size.');
37033 if(markerBytes == 0xffe1){
37034 _this.parseExifData(
37041 offset += markerLength;
37051 var url = _this.urlAPI.createObjectURL(_this.file);
37053 _this.loadCanvas(url);
37058 reader.readAsArrayBuffer(this.file);
37064 parseExifData : function(dataView, offset, length)
37066 var tiffOffset = offset + 10,
37070 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37071 // No Exif data, might be XMP data instead
37075 // Check for the ASCII code for "Exif" (0x45786966):
37076 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37077 // No Exif data, might be XMP data instead
37080 if (tiffOffset + 8 > dataView.byteLength) {
37081 Roo.log('Invalid Exif data: Invalid segment size.');
37084 // Check for the two null bytes:
37085 if (dataView.getUint16(offset + 8) !== 0x0000) {
37086 Roo.log('Invalid Exif data: Missing byte alignment offset.');
37089 // Check the byte alignment:
37090 switch (dataView.getUint16(tiffOffset)) {
37092 littleEndian = true;
37095 littleEndian = false;
37098 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37101 // Check for the TIFF tag marker (0x002A):
37102 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37103 Roo.log('Invalid Exif data: Missing TIFF marker.');
37106 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37107 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37109 this.parseExifTags(
37112 tiffOffset + dirOffset,
37117 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37122 if (dirOffset + 6 > dataView.byteLength) {
37123 Roo.log('Invalid Exif data: Invalid directory offset.');
37126 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37127 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37128 if (dirEndOffset + 4 > dataView.byteLength) {
37129 Roo.log('Invalid Exif data: Invalid directory size.');
37132 for (i = 0; i < tagsNumber; i += 1) {
37136 dirOffset + 2 + 12 * i, // tag offset
37140 // Return the offset to the next directory:
37141 return dataView.getUint32(dirEndOffset, littleEndian);
37144 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
37146 var tag = dataView.getUint16(offset, littleEndian);
37148 this.exif[tag] = this.getExifValue(
37152 dataView.getUint16(offset + 2, littleEndian), // tag type
37153 dataView.getUint32(offset + 4, littleEndian), // tag length
37158 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37160 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37169 Roo.log('Invalid Exif data: Invalid tag type.');
37173 tagSize = tagType.size * length;
37174 // Determine if the value is contained in the dataOffset bytes,
37175 // or if the value at the dataOffset is a pointer to the actual data:
37176 dataOffset = tagSize > 4 ?
37177 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37178 if (dataOffset + tagSize > dataView.byteLength) {
37179 Roo.log('Invalid Exif data: Invalid data offset.');
37182 if (length === 1) {
37183 return tagType.getValue(dataView, dataOffset, littleEndian);
37186 for (i = 0; i < length; i += 1) {
37187 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37190 if (tagType.ascii) {
37192 // Concatenate the chars:
37193 for (i = 0; i < values.length; i += 1) {
37195 // Ignore the terminating NULL byte(s):
37196 if (c === '\u0000') {
37208 Roo.apply(Roo.bootstrap.UploadCropbox, {
37210 'Orientation': 0x0112
37214 1: 0, //'top-left',
37216 3: 180, //'bottom-right',
37217 // 4: 'bottom-left',
37219 6: 90, //'right-top',
37220 // 7: 'right-bottom',
37221 8: 270 //'left-bottom'
37225 // byte, 8-bit unsigned int:
37227 getValue: function (dataView, dataOffset) {
37228 return dataView.getUint8(dataOffset);
37232 // ascii, 8-bit byte:
37234 getValue: function (dataView, dataOffset) {
37235 return String.fromCharCode(dataView.getUint8(dataOffset));
37240 // short, 16 bit int:
37242 getValue: function (dataView, dataOffset, littleEndian) {
37243 return dataView.getUint16(dataOffset, littleEndian);
37247 // long, 32 bit int:
37249 getValue: function (dataView, dataOffset, littleEndian) {
37250 return dataView.getUint32(dataOffset, littleEndian);
37254 // rational = two long values, first is numerator, second is denominator:
37256 getValue: function (dataView, dataOffset, littleEndian) {
37257 return dataView.getUint32(dataOffset, littleEndian) /
37258 dataView.getUint32(dataOffset + 4, littleEndian);
37262 // slong, 32 bit signed int:
37264 getValue: function (dataView, dataOffset, littleEndian) {
37265 return dataView.getInt32(dataOffset, littleEndian);
37269 // srational, two slongs, first is numerator, second is denominator:
37271 getValue: function (dataView, dataOffset, littleEndian) {
37272 return dataView.getInt32(dataOffset, littleEndian) /
37273 dataView.getInt32(dataOffset + 4, littleEndian);
37283 cls : 'btn-group roo-upload-cropbox-rotate-left',
37284 action : 'rotate-left',
37288 cls : 'btn btn-default',
37289 html : '<i class="fa fa-undo"></i>'
37295 cls : 'btn-group roo-upload-cropbox-picture',
37296 action : 'picture',
37300 cls : 'btn btn-default',
37301 html : '<i class="fa fa-picture-o"></i>'
37307 cls : 'btn-group roo-upload-cropbox-rotate-right',
37308 action : 'rotate-right',
37312 cls : 'btn btn-default',
37313 html : '<i class="fa fa-repeat"></i>'
37321 cls : 'btn-group roo-upload-cropbox-rotate-left',
37322 action : 'rotate-left',
37326 cls : 'btn btn-default',
37327 html : '<i class="fa fa-undo"></i>'
37333 cls : 'btn-group roo-upload-cropbox-download',
37334 action : 'download',
37338 cls : 'btn btn-default',
37339 html : '<i class="fa fa-download"></i>'
37345 cls : 'btn-group roo-upload-cropbox-crop',
37350 cls : 'btn btn-default',
37351 html : '<i class="fa fa-crop"></i>'
37357 cls : 'btn-group roo-upload-cropbox-trash',
37362 cls : 'btn btn-default',
37363 html : '<i class="fa fa-trash"></i>'
37369 cls : 'btn-group roo-upload-cropbox-rotate-right',
37370 action : 'rotate-right',
37374 cls : 'btn btn-default',
37375 html : '<i class="fa fa-repeat"></i>'
37383 cls : 'btn-group roo-upload-cropbox-rotate-left',
37384 action : 'rotate-left',
37388 cls : 'btn btn-default',
37389 html : '<i class="fa fa-undo"></i>'
37395 cls : 'btn-group roo-upload-cropbox-rotate-right',
37396 action : 'rotate-right',
37400 cls : 'btn btn-default',
37401 html : '<i class="fa fa-repeat"></i>'
37414 * @class Roo.bootstrap.DocumentManager
37415 * @extends Roo.bootstrap.Component
37416 * Bootstrap DocumentManager class
37417 * @cfg {String} paramName default 'imageUpload'
37418 * @cfg {String} toolTipName default 'filename'
37419 * @cfg {String} method default POST
37420 * @cfg {String} url action url
37421 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37422 * @cfg {Boolean} multiple multiple upload default true
37423 * @cfg {Number} thumbSize default 300
37424 * @cfg {String} fieldLabel
37425 * @cfg {Number} labelWidth default 4
37426 * @cfg {String} labelAlign (left|top) default left
37427 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37428 * @cfg {Number} labellg set the width of label (1-12)
37429 * @cfg {Number} labelmd set the width of label (1-12)
37430 * @cfg {Number} labelsm set the width of label (1-12)
37431 * @cfg {Number} labelxs set the width of label (1-12)
37434 * Create a new DocumentManager
37435 * @param {Object} config The config object
37438 Roo.bootstrap.DocumentManager = function(config){
37439 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37442 this.delegates = [];
37447 * Fire when initial the DocumentManager
37448 * @param {Roo.bootstrap.DocumentManager} this
37453 * inspect selected file
37454 * @param {Roo.bootstrap.DocumentManager} this
37455 * @param {File} file
37460 * Fire when xhr load exception
37461 * @param {Roo.bootstrap.DocumentManager} this
37462 * @param {XMLHttpRequest} xhr
37464 "exception" : true,
37466 * @event afterupload
37467 * Fire when xhr load exception
37468 * @param {Roo.bootstrap.DocumentManager} this
37469 * @param {XMLHttpRequest} xhr
37471 "afterupload" : true,
37474 * prepare the form data
37475 * @param {Roo.bootstrap.DocumentManager} this
37476 * @param {Object} formData
37481 * Fire when remove the file
37482 * @param {Roo.bootstrap.DocumentManager} this
37483 * @param {Object} file
37488 * Fire after refresh the file
37489 * @param {Roo.bootstrap.DocumentManager} this
37494 * Fire after click the image
37495 * @param {Roo.bootstrap.DocumentManager} this
37496 * @param {Object} file
37501 * Fire when upload a image and editable set to true
37502 * @param {Roo.bootstrap.DocumentManager} this
37503 * @param {Object} file
37507 * @event beforeselectfile
37508 * Fire before select file
37509 * @param {Roo.bootstrap.DocumentManager} this
37511 "beforeselectfile" : true,
37514 * Fire before process file
37515 * @param {Roo.bootstrap.DocumentManager} this
37516 * @param {Object} file
37520 * @event previewrendered
37521 * Fire when preview rendered
37522 * @param {Roo.bootstrap.DocumentManager} this
37523 * @param {Object} file
37525 "previewrendered" : true,
37528 "previewResize" : true
37533 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
37542 paramName : 'imageUpload',
37543 toolTipName : 'filename',
37546 labelAlign : 'left',
37556 getAutoCreate : function()
37558 var managerWidget = {
37560 cls : 'roo-document-manager',
37564 cls : 'roo-document-manager-selector',
37569 cls : 'roo-document-manager-uploader',
37573 cls : 'roo-document-manager-upload-btn',
37574 html : '<i class="fa fa-plus"></i>'
37585 cls : 'column col-md-12',
37590 if(this.fieldLabel.length){
37595 cls : 'column col-md-12',
37596 html : this.fieldLabel
37600 cls : 'column col-md-12',
37605 if(this.labelAlign == 'left'){
37610 html : this.fieldLabel
37619 if(this.labelWidth > 12){
37620 content[0].style = "width: " + this.labelWidth + 'px';
37623 if(this.labelWidth < 13 && this.labelmd == 0){
37624 this.labelmd = this.labelWidth;
37627 if(this.labellg > 0){
37628 content[0].cls += ' col-lg-' + this.labellg;
37629 content[1].cls += ' col-lg-' + (12 - this.labellg);
37632 if(this.labelmd > 0){
37633 content[0].cls += ' col-md-' + this.labelmd;
37634 content[1].cls += ' col-md-' + (12 - this.labelmd);
37637 if(this.labelsm > 0){
37638 content[0].cls += ' col-sm-' + this.labelsm;
37639 content[1].cls += ' col-sm-' + (12 - this.labelsm);
37642 if(this.labelxs > 0){
37643 content[0].cls += ' col-xs-' + this.labelxs;
37644 content[1].cls += ' col-xs-' + (12 - this.labelxs);
37652 cls : 'row clearfix',
37660 initEvents : function()
37662 this.managerEl = this.el.select('.roo-document-manager', true).first();
37663 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37665 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37666 this.selectorEl.hide();
37669 this.selectorEl.attr('multiple', 'multiple');
37672 this.selectorEl.on('change', this.onFileSelected, this);
37674 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37675 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37677 this.uploader.on('click', this.onUploaderClick, this);
37679 this.renderProgressDialog();
37683 window.addEventListener("resize", function() { _this.refresh(); } );
37685 this.fireEvent('initial', this);
37688 renderProgressDialog : function()
37692 this.progressDialog = new Roo.bootstrap.Modal({
37693 cls : 'roo-document-manager-progress-dialog',
37694 allow_close : false,
37705 btnclick : function() {
37706 _this.uploadCancel();
37712 this.progressDialog.render(Roo.get(document.body));
37714 this.progress = new Roo.bootstrap.Progress({
37715 cls : 'roo-document-manager-progress',
37720 this.progress.render(this.progressDialog.getChildContainer());
37722 this.progressBar = new Roo.bootstrap.ProgressBar({
37723 cls : 'roo-document-manager-progress-bar',
37726 aria_valuemax : 12,
37730 this.progressBar.render(this.progress.getChildContainer());
37733 onUploaderClick : function(e)
37735 e.preventDefault();
37737 if(this.fireEvent('beforeselectfile', this) != false){
37738 this.selectorEl.dom.click();
37743 onFileSelected : function(e)
37745 e.preventDefault();
37747 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37751 Roo.each(this.selectorEl.dom.files, function(file){
37752 if(this.fireEvent('inspect', this, file) != false){
37753 this.files.push(file);
37763 this.selectorEl.dom.value = '';
37765 if(!this.files || !this.files.length){
37769 if(this.boxes > 0 && this.files.length > this.boxes){
37770 this.files = this.files.slice(0, this.boxes);
37773 this.uploader.show();
37775 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37776 this.uploader.hide();
37785 Roo.each(this.files, function(file){
37787 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37788 var f = this.renderPreview(file);
37793 if(file.type.indexOf('image') != -1){
37794 this.delegates.push(
37796 _this.process(file);
37797 }).createDelegate(this)
37805 _this.process(file);
37806 }).createDelegate(this)
37811 this.files = files;
37813 this.delegates = this.delegates.concat(docs);
37815 if(!this.delegates.length){
37820 this.progressBar.aria_valuemax = this.delegates.length;
37827 arrange : function()
37829 if(!this.delegates.length){
37830 this.progressDialog.hide();
37835 var delegate = this.delegates.shift();
37837 this.progressDialog.show();
37839 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37841 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37846 refresh : function()
37848 this.uploader.show();
37850 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37851 this.uploader.hide();
37854 Roo.isTouch ? this.closable(false) : this.closable(true);
37856 this.fireEvent('refresh', this);
37859 onRemove : function(e, el, o)
37861 e.preventDefault();
37863 this.fireEvent('remove', this, o);
37867 remove : function(o)
37871 Roo.each(this.files, function(file){
37872 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37881 this.files = files;
37888 Roo.each(this.files, function(file){
37893 file.target.remove();
37902 onClick : function(e, el, o)
37904 e.preventDefault();
37906 this.fireEvent('click', this, o);
37910 closable : function(closable)
37912 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37914 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37926 xhrOnLoad : function(xhr)
37928 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37932 if (xhr.readyState !== 4) {
37934 this.fireEvent('exception', this, xhr);
37938 var response = Roo.decode(xhr.responseText);
37940 if(!response.success){
37942 this.fireEvent('exception', this, xhr);
37946 var file = this.renderPreview(response.data);
37948 this.files.push(file);
37952 this.fireEvent('afterupload', this, xhr);
37956 xhrOnError : function(xhr)
37958 Roo.log('xhr on error');
37960 var response = Roo.decode(xhr.responseText);
37967 process : function(file)
37969 if(this.fireEvent('process', this, file) !== false){
37970 if(this.editable && file.type.indexOf('image') != -1){
37971 this.fireEvent('edit', this, file);
37975 this.uploadStart(file, false);
37982 uploadStart : function(file, crop)
37984 this.xhr = new XMLHttpRequest();
37986 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37991 file.xhr = this.xhr;
37993 this.managerEl.createChild({
37995 cls : 'roo-document-manager-loading',
37999 tooltip : file.name,
38000 cls : 'roo-document-manager-thumb',
38001 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38007 this.xhr.open(this.method, this.url, true);
38010 "Accept": "application/json",
38011 "Cache-Control": "no-cache",
38012 "X-Requested-With": "XMLHttpRequest"
38015 for (var headerName in headers) {
38016 var headerValue = headers[headerName];
38018 this.xhr.setRequestHeader(headerName, headerValue);
38024 this.xhr.onload = function()
38026 _this.xhrOnLoad(_this.xhr);
38029 this.xhr.onerror = function()
38031 _this.xhrOnError(_this.xhr);
38034 var formData = new FormData();
38036 formData.append('returnHTML', 'NO');
38039 formData.append('crop', crop);
38042 formData.append(this.paramName, file, file.name);
38049 if(this.fireEvent('prepare', this, formData, options) != false){
38051 if(options.manually){
38055 this.xhr.send(formData);
38059 this.uploadCancel();
38062 uploadCancel : function()
38068 this.delegates = [];
38070 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38077 renderPreview : function(file)
38079 if(typeof(file.target) != 'undefined' && file.target){
38083 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38085 var previewEl = this.managerEl.createChild({
38087 cls : 'roo-document-manager-preview',
38091 tooltip : file[this.toolTipName],
38092 cls : 'roo-document-manager-thumb',
38093 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38098 html : '<i class="fa fa-times-circle"></i>'
38103 var close = previewEl.select('button.close', true).first();
38105 close.on('click', this.onRemove, this, file);
38107 file.target = previewEl;
38109 var image = previewEl.select('img', true).first();
38113 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38115 image.on('click', this.onClick, this, file);
38117 this.fireEvent('previewrendered', this, file);
38123 onPreviewLoad : function(file, image)
38125 if(typeof(file.target) == 'undefined' || !file.target){
38129 var width = image.dom.naturalWidth || image.dom.width;
38130 var height = image.dom.naturalHeight || image.dom.height;
38132 if(!this.previewResize) {
38136 if(width > height){
38137 file.target.addClass('wide');
38141 file.target.addClass('tall');
38146 uploadFromSource : function(file, crop)
38148 this.xhr = new XMLHttpRequest();
38150 this.managerEl.createChild({
38152 cls : 'roo-document-manager-loading',
38156 tooltip : file.name,
38157 cls : 'roo-document-manager-thumb',
38158 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38164 this.xhr.open(this.method, this.url, true);
38167 "Accept": "application/json",
38168 "Cache-Control": "no-cache",
38169 "X-Requested-With": "XMLHttpRequest"
38172 for (var headerName in headers) {
38173 var headerValue = headers[headerName];
38175 this.xhr.setRequestHeader(headerName, headerValue);
38181 this.xhr.onload = function()
38183 _this.xhrOnLoad(_this.xhr);
38186 this.xhr.onerror = function()
38188 _this.xhrOnError(_this.xhr);
38191 var formData = new FormData();
38193 formData.append('returnHTML', 'NO');
38195 formData.append('crop', crop);
38197 if(typeof(file.filename) != 'undefined'){
38198 formData.append('filename', file.filename);
38201 if(typeof(file.mimetype) != 'undefined'){
38202 formData.append('mimetype', file.mimetype);
38207 if(this.fireEvent('prepare', this, formData) != false){
38208 this.xhr.send(formData);
38218 * @class Roo.bootstrap.DocumentViewer
38219 * @extends Roo.bootstrap.Component
38220 * Bootstrap DocumentViewer class
38221 * @cfg {Boolean} showDownload (true|false) show download button (default true)
38222 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38225 * Create a new DocumentViewer
38226 * @param {Object} config The config object
38229 Roo.bootstrap.DocumentViewer = function(config){
38230 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38235 * Fire after initEvent
38236 * @param {Roo.bootstrap.DocumentViewer} this
38242 * @param {Roo.bootstrap.DocumentViewer} this
38247 * Fire after download button
38248 * @param {Roo.bootstrap.DocumentViewer} this
38253 * Fire after trash button
38254 * @param {Roo.bootstrap.DocumentViewer} this
38261 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
38263 showDownload : true,
38267 getAutoCreate : function()
38271 cls : 'roo-document-viewer',
38275 cls : 'roo-document-viewer-body',
38279 cls : 'roo-document-viewer-thumb',
38283 cls : 'roo-document-viewer-image'
38291 cls : 'roo-document-viewer-footer',
38294 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38298 cls : 'btn-group roo-document-viewer-download',
38302 cls : 'btn btn-default',
38303 html : '<i class="fa fa-download"></i>'
38309 cls : 'btn-group roo-document-viewer-trash',
38313 cls : 'btn btn-default',
38314 html : '<i class="fa fa-trash"></i>'
38327 initEvents : function()
38329 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38330 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38332 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38333 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38335 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38336 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38338 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38339 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38341 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38342 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38344 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38345 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38347 this.bodyEl.on('click', this.onClick, this);
38348 this.downloadBtn.on('click', this.onDownload, this);
38349 this.trashBtn.on('click', this.onTrash, this);
38351 this.downloadBtn.hide();
38352 this.trashBtn.hide();
38354 if(this.showDownload){
38355 this.downloadBtn.show();
38358 if(this.showTrash){
38359 this.trashBtn.show();
38362 if(!this.showDownload && !this.showTrash) {
38363 this.footerEl.hide();
38368 initial : function()
38370 this.fireEvent('initial', this);
38374 onClick : function(e)
38376 e.preventDefault();
38378 this.fireEvent('click', this);
38381 onDownload : function(e)
38383 e.preventDefault();
38385 this.fireEvent('download', this);
38388 onTrash : function(e)
38390 e.preventDefault();
38392 this.fireEvent('trash', this);
38404 * @class Roo.bootstrap.form.FieldLabel
38405 * @extends Roo.bootstrap.Component
38406 * Bootstrap FieldLabel class
38407 * @cfg {String} html contents of the element
38408 * @cfg {String} tag tag of the element default label
38409 * @cfg {String} cls class of the element
38410 * @cfg {String} target label target
38411 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38412 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38413 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38414 * @cfg {String} iconTooltip default "This field is required"
38415 * @cfg {String} indicatorpos (left|right) default left
38418 * Create a new FieldLabel
38419 * @param {Object} config The config object
38422 Roo.bootstrap.form.FieldLabel = function(config){
38423 Roo.bootstrap.Element.superclass.constructor.call(this, config);
38428 * Fires after the field has been marked as invalid.
38429 * @param {Roo.form.FieldLabel} this
38430 * @param {String} msg The validation message
38435 * Fires after the field has been validated with no errors.
38436 * @param {Roo.form.FieldLabel} this
38442 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
38449 invalidClass : 'has-warning',
38450 validClass : 'has-success',
38451 iconTooltip : 'This field is required',
38452 indicatorpos : 'left',
38454 getAutoCreate : function(){
38457 if (!this.allowBlank) {
38463 cls : 'roo-bootstrap-field-label ' + this.cls,
38468 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38469 tooltip : this.iconTooltip
38478 if(this.indicatorpos == 'right'){
38481 cls : 'roo-bootstrap-field-label ' + this.cls,
38490 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38491 tooltip : this.iconTooltip
38500 initEvents: function()
38502 Roo.bootstrap.Element.superclass.initEvents.call(this);
38504 this.indicator = this.indicatorEl();
38506 if(this.indicator){
38507 this.indicator.removeClass('visible');
38508 this.indicator.addClass('invisible');
38511 Roo.bootstrap.form.FieldLabel.register(this);
38514 indicatorEl : function()
38516 var indicator = this.el.select('i.roo-required-indicator',true).first();
38527 * Mark this field as valid
38529 markValid : function()
38531 if(this.indicator){
38532 this.indicator.removeClass('visible');
38533 this.indicator.addClass('invisible');
38535 if (Roo.bootstrap.version == 3) {
38536 this.el.removeClass(this.invalidClass);
38537 this.el.addClass(this.validClass);
38539 this.el.removeClass('is-invalid');
38540 this.el.addClass('is-valid');
38544 this.fireEvent('valid', this);
38548 * Mark this field as invalid
38549 * @param {String} msg The validation message
38551 markInvalid : function(msg)
38553 if(this.indicator){
38554 this.indicator.removeClass('invisible');
38555 this.indicator.addClass('visible');
38557 if (Roo.bootstrap.version == 3) {
38558 this.el.removeClass(this.validClass);
38559 this.el.addClass(this.invalidClass);
38561 this.el.removeClass('is-valid');
38562 this.el.addClass('is-invalid');
38566 this.fireEvent('invalid', this, msg);
38572 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38577 * register a FieldLabel Group
38578 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38580 register : function(label)
38582 if(this.groups.hasOwnProperty(label.target)){
38586 this.groups[label.target] = label;
38590 * fetch a FieldLabel Group based on the target
38591 * @param {string} target
38592 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38594 get: function(target) {
38595 if (typeof(this.groups[target]) == 'undefined') {
38599 return this.groups[target] ;
38608 * page DateSplitField.
38614 * @class Roo.bootstrap.form.DateSplitField
38615 * @extends Roo.bootstrap.Component
38616 * Bootstrap DateSplitField class
38617 * @cfg {string} fieldLabel - the label associated
38618 * @cfg {Number} labelWidth set the width of label (0-12)
38619 * @cfg {String} labelAlign (top|left)
38620 * @cfg {Boolean} dayAllowBlank (true|false) default false
38621 * @cfg {Boolean} monthAllowBlank (true|false) default false
38622 * @cfg {Boolean} yearAllowBlank (true|false) default false
38623 * @cfg {string} dayPlaceholder
38624 * @cfg {string} monthPlaceholder
38625 * @cfg {string} yearPlaceholder
38626 * @cfg {string} dayFormat default 'd'
38627 * @cfg {string} monthFormat default 'm'
38628 * @cfg {string} yearFormat default 'Y'
38629 * @cfg {Number} labellg set the width of label (1-12)
38630 * @cfg {Number} labelmd set the width of label (1-12)
38631 * @cfg {Number} labelsm set the width of label (1-12)
38632 * @cfg {Number} labelxs set the width of label (1-12)
38636 * Create a new DateSplitField
38637 * @param {Object} config The config object
38640 Roo.bootstrap.form.DateSplitField = function(config){
38641 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38647 * getting the data of years
38648 * @param {Roo.bootstrap.form.DateSplitField} this
38649 * @param {Object} years
38654 * getting the data of days
38655 * @param {Roo.bootstrap.form.DateSplitField} this
38656 * @param {Object} days
38661 * Fires after the field has been marked as invalid.
38662 * @param {Roo.form.Field} this
38663 * @param {String} msg The validation message
38668 * Fires after the field has been validated with no errors.
38669 * @param {Roo.form.Field} this
38675 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
38678 labelAlign : 'top',
38680 dayAllowBlank : false,
38681 monthAllowBlank : false,
38682 yearAllowBlank : false,
38683 dayPlaceholder : '',
38684 monthPlaceholder : '',
38685 yearPlaceholder : '',
38689 isFormField : true,
38695 getAutoCreate : function()
38699 cls : 'row roo-date-split-field-group',
38704 cls : 'form-hidden-field roo-date-split-field-group-value',
38710 var labelCls = 'col-md-12';
38711 var contentCls = 'col-md-4';
38713 if(this.fieldLabel){
38717 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38721 html : this.fieldLabel
38726 if(this.labelAlign == 'left'){
38728 if(this.labelWidth > 12){
38729 label.style = "width: " + this.labelWidth + 'px';
38732 if(this.labelWidth < 13 && this.labelmd == 0){
38733 this.labelmd = this.labelWidth;
38736 if(this.labellg > 0){
38737 labelCls = ' col-lg-' + this.labellg;
38738 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38741 if(this.labelmd > 0){
38742 labelCls = ' col-md-' + this.labelmd;
38743 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38746 if(this.labelsm > 0){
38747 labelCls = ' col-sm-' + this.labelsm;
38748 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38751 if(this.labelxs > 0){
38752 labelCls = ' col-xs-' + this.labelxs;
38753 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38757 label.cls += ' ' + labelCls;
38759 cfg.cn.push(label);
38762 Roo.each(['day', 'month', 'year'], function(t){
38765 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38772 inputEl: function ()
38774 return this.el.select('.roo-date-split-field-group-value', true).first();
38777 onRender : function(ct, position)
38781 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38783 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38785 this.dayField = new Roo.bootstrap.form.ComboBox({
38786 allowBlank : this.dayAllowBlank,
38787 alwaysQuery : true,
38788 displayField : 'value',
38791 forceSelection : true,
38793 placeholder : this.dayPlaceholder,
38794 selectOnFocus : true,
38795 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38796 triggerAction : 'all',
38798 valueField : 'value',
38799 store : new Roo.data.SimpleStore({
38800 data : (function() {
38802 _this.fireEvent('days', _this, days);
38805 fields : [ 'value' ]
38808 select : function (_self, record, index)
38810 _this.setValue(_this.getValue());
38815 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38817 this.monthField = new Roo.bootstrap.form.MonthField({
38818 after : '<i class=\"fa fa-calendar\"></i>',
38819 allowBlank : this.monthAllowBlank,
38820 placeholder : this.monthPlaceholder,
38823 render : function (_self)
38825 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38826 e.preventDefault();
38830 select : function (_self, oldvalue, newvalue)
38832 _this.setValue(_this.getValue());
38837 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38839 this.yearField = new Roo.bootstrap.form.ComboBox({
38840 allowBlank : this.yearAllowBlank,
38841 alwaysQuery : true,
38842 displayField : 'value',
38845 forceSelection : true,
38847 placeholder : this.yearPlaceholder,
38848 selectOnFocus : true,
38849 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38850 triggerAction : 'all',
38852 valueField : 'value',
38853 store : new Roo.data.SimpleStore({
38854 data : (function() {
38856 _this.fireEvent('years', _this, years);
38859 fields : [ 'value' ]
38862 select : function (_self, record, index)
38864 _this.setValue(_this.getValue());
38869 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38872 setValue : function(v, format)
38874 this.inputEl.dom.value = v;
38876 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38878 var d = Date.parseDate(v, f);
38885 this.setDay(d.format(this.dayFormat));
38886 this.setMonth(d.format(this.monthFormat));
38887 this.setYear(d.format(this.yearFormat));
38894 setDay : function(v)
38896 this.dayField.setValue(v);
38897 this.inputEl.dom.value = this.getValue();
38902 setMonth : function(v)
38904 this.monthField.setValue(v, true);
38905 this.inputEl.dom.value = this.getValue();
38910 setYear : function(v)
38912 this.yearField.setValue(v);
38913 this.inputEl.dom.value = this.getValue();
38918 getDay : function()
38920 return this.dayField.getValue();
38923 getMonth : function()
38925 return this.monthField.getValue();
38928 getYear : function()
38930 return this.yearField.getValue();
38933 getValue : function()
38935 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38937 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38947 this.inputEl.dom.value = '';
38952 validate : function()
38954 var d = this.dayField.validate();
38955 var m = this.monthField.validate();
38956 var y = this.yearField.validate();
38961 (!this.dayAllowBlank && !d) ||
38962 (!this.monthAllowBlank && !m) ||
38963 (!this.yearAllowBlank && !y)
38968 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38977 this.markInvalid();
38982 markValid : function()
38985 var label = this.el.select('label', true).first();
38986 var icon = this.el.select('i.fa-star', true).first();
38992 this.fireEvent('valid', this);
38996 * Mark this field as invalid
38997 * @param {String} msg The validation message
38999 markInvalid : function(msg)
39002 var label = this.el.select('label', true).first();
39003 var icon = this.el.select('i.fa-star', true).first();
39005 if(label && !icon){
39006 this.el.select('.roo-date-split-field-label', true).createChild({
39008 cls : 'text-danger fa fa-lg fa-star',
39009 tooltip : 'This field is required',
39010 style : 'margin-right:5px;'
39014 this.fireEvent('invalid', this, msg);
39017 clearInvalid : function()
39019 var label = this.el.select('label', true).first();
39020 var icon = this.el.select('i.fa-star', true).first();
39026 this.fireEvent('valid', this);
39029 getName: function()
39039 * @class Roo.bootstrap.LayoutMasonry
39040 * @extends Roo.bootstrap.Component
39041 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39042 * Bootstrap Layout Masonry class
39045 * http://masonry.desandro.com
39047 * The idea is to render all the bricks based on vertical width...
39049 * The original code extends 'outlayer' - we might need to use that....
39052 * Create a new Element
39053 * @param {Object} config The config object
39056 Roo.bootstrap.LayoutMasonry = function(config){
39058 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39062 Roo.bootstrap.LayoutMasonry.register(this);
39068 * Fire after layout the items
39069 * @param {Roo.bootstrap.LayoutMasonry} this
39070 * @param {Roo.EventObject} e
39077 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
39080 * @cfg {Boolean} isLayoutInstant = no animation?
39082 isLayoutInstant : false, // needed?
39085 * @cfg {Number} boxWidth width of the columns
39090 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
39095 * @cfg {Number} padWidth padding below box..
39100 * @cfg {Number} gutter gutter width..
39105 * @cfg {Number} maxCols maximum number of columns
39111 * @cfg {Boolean} isAutoInitial defalut true
39113 isAutoInitial : true,
39118 * @cfg {Boolean} isHorizontal defalut false
39120 isHorizontal : false,
39122 currentSize : null,
39128 bricks: null, //CompositeElement
39132 _isLayoutInited : false,
39134 // isAlternative : false, // only use for vertical layout...
39137 * @cfg {Number} alternativePadWidth padding below box..
39139 alternativePadWidth : 50,
39141 selectedBrick : [],
39143 getAutoCreate : function(){
39145 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39149 cls: 'blog-masonary-wrapper ' + this.cls,
39151 cls : 'mas-boxes masonary'
39158 getChildContainer: function( )
39160 if (this.boxesEl) {
39161 return this.boxesEl;
39164 this.boxesEl = this.el.select('.mas-boxes').first();
39166 return this.boxesEl;
39170 initEvents : function()
39174 if(this.isAutoInitial){
39175 Roo.log('hook children rendered');
39176 this.on('childrenrendered', function() {
39177 Roo.log('children rendered');
39183 initial : function()
39185 this.selectedBrick = [];
39187 this.currentSize = this.el.getBox(true);
39189 Roo.EventManager.onWindowResize(this.resize, this);
39191 if(!this.isAutoInitial){
39199 //this.layout.defer(500,this);
39203 resize : function()
39205 var cs = this.el.getBox(true);
39208 this.currentSize.width == cs.width &&
39209 this.currentSize.x == cs.x &&
39210 this.currentSize.height == cs.height &&
39211 this.currentSize.y == cs.y
39213 Roo.log("no change in with or X or Y");
39217 this.currentSize = cs;
39223 layout : function()
39225 this._resetLayout();
39227 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39229 this.layoutItems( isInstant );
39231 this._isLayoutInited = true;
39233 this.fireEvent('layout', this);
39237 _resetLayout : function()
39239 if(this.isHorizontal){
39240 this.horizontalMeasureColumns();
39244 this.verticalMeasureColumns();
39248 verticalMeasureColumns : function()
39250 this.getContainerWidth();
39252 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39253 // this.colWidth = Math.floor(this.containerWidth * 0.8);
39257 var boxWidth = this.boxWidth + this.padWidth;
39259 if(this.containerWidth < this.boxWidth){
39260 boxWidth = this.containerWidth
39263 var containerWidth = this.containerWidth;
39265 var cols = Math.floor(containerWidth / boxWidth);
39267 this.cols = Math.max( cols, 1 );
39269 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39271 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39273 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39275 this.colWidth = boxWidth + avail - this.padWidth;
39277 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39278 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
39281 horizontalMeasureColumns : function()
39283 this.getContainerWidth();
39285 var boxWidth = this.boxWidth;
39287 if(this.containerWidth < boxWidth){
39288 boxWidth = this.containerWidth;
39291 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39293 this.el.setHeight(boxWidth);
39297 getContainerWidth : function()
39299 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
39302 layoutItems : function( isInstant )
39304 Roo.log(this.bricks);
39306 var items = Roo.apply([], this.bricks);
39308 if(this.isHorizontal){
39309 this._horizontalLayoutItems( items , isInstant );
39313 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39314 // this._verticalAlternativeLayoutItems( items , isInstant );
39318 this._verticalLayoutItems( items , isInstant );
39322 _verticalLayoutItems : function ( items , isInstant)
39324 if ( !items || !items.length ) {
39329 ['xs', 'xs', 'xs', 'tall'],
39330 ['xs', 'xs', 'tall'],
39331 ['xs', 'xs', 'sm'],
39332 ['xs', 'xs', 'xs'],
39338 ['sm', 'xs', 'xs'],
39342 ['tall', 'xs', 'xs', 'xs'],
39343 ['tall', 'xs', 'xs'],
39355 Roo.each(items, function(item, k){
39357 switch (item.size) {
39358 // these layouts take up a full box,
39369 boxes.push([item]);
39392 var filterPattern = function(box, length)
39400 var pattern = box.slice(0, length);
39404 Roo.each(pattern, function(i){
39405 format.push(i.size);
39408 Roo.each(standard, function(s){
39410 if(String(s) != String(format)){
39419 if(!match && length == 1){
39424 filterPattern(box, length - 1);
39428 queue.push(pattern);
39430 box = box.slice(length, box.length);
39432 filterPattern(box, 4);
39438 Roo.each(boxes, function(box, k){
39444 if(box.length == 1){
39449 filterPattern(box, 4);
39453 this._processVerticalLayoutQueue( queue, isInstant );
39457 // _verticalAlternativeLayoutItems : function( items , isInstant )
39459 // if ( !items || !items.length ) {
39463 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
39467 _horizontalLayoutItems : function ( items , isInstant)
39469 if ( !items || !items.length || items.length < 3) {
39475 var eItems = items.slice(0, 3);
39477 items = items.slice(3, items.length);
39480 ['xs', 'xs', 'xs', 'wide'],
39481 ['xs', 'xs', 'wide'],
39482 ['xs', 'xs', 'sm'],
39483 ['xs', 'xs', 'xs'],
39489 ['sm', 'xs', 'xs'],
39493 ['wide', 'xs', 'xs', 'xs'],
39494 ['wide', 'xs', 'xs'],
39507 Roo.each(items, function(item, k){
39509 switch (item.size) {
39520 boxes.push([item]);
39544 var filterPattern = function(box, length)
39552 var pattern = box.slice(0, length);
39556 Roo.each(pattern, function(i){
39557 format.push(i.size);
39560 Roo.each(standard, function(s){
39562 if(String(s) != String(format)){
39571 if(!match && length == 1){
39576 filterPattern(box, length - 1);
39580 queue.push(pattern);
39582 box = box.slice(length, box.length);
39584 filterPattern(box, 4);
39590 Roo.each(boxes, function(box, k){
39596 if(box.length == 1){
39601 filterPattern(box, 4);
39608 var pos = this.el.getBox(true);
39612 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39614 var hit_end = false;
39616 Roo.each(queue, function(box){
39620 Roo.each(box, function(b){
39622 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39632 Roo.each(box, function(b){
39634 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39637 mx = Math.max(mx, b.x);
39641 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39645 Roo.each(box, function(b){
39647 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39661 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39664 /** Sets position of item in DOM
39665 * @param {Element} item
39666 * @param {Number} x - horizontal position
39667 * @param {Number} y - vertical position
39668 * @param {Boolean} isInstant - disables transitions
39670 _processVerticalLayoutQueue : function( queue, isInstant )
39672 var pos = this.el.getBox(true);
39677 for (var i = 0; i < this.cols; i++){
39681 Roo.each(queue, function(box, k){
39683 var col = k % this.cols;
39685 Roo.each(box, function(b,kk){
39687 b.el.position('absolute');
39689 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39690 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39692 if(b.size == 'md-left' || b.size == 'md-right'){
39693 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39694 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39697 b.el.setWidth(width);
39698 b.el.setHeight(height);
39700 b.el.select('iframe',true).setSize(width,height);
39704 for (var i = 0; i < this.cols; i++){
39706 if(maxY[i] < maxY[col]){
39711 col = Math.min(col, i);
39715 x = pos.x + col * (this.colWidth + this.padWidth);
39719 var positions = [];
39721 switch (box.length){
39723 positions = this.getVerticalOneBoxColPositions(x, y, box);
39726 positions = this.getVerticalTwoBoxColPositions(x, y, box);
39729 positions = this.getVerticalThreeBoxColPositions(x, y, box);
39732 positions = this.getVerticalFourBoxColPositions(x, y, box);
39738 Roo.each(box, function(b,kk){
39740 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39742 var sz = b.el.getSize();
39744 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39752 for (var i = 0; i < this.cols; i++){
39753 mY = Math.max(mY, maxY[i]);
39756 this.el.setHeight(mY - pos.y);
39760 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39762 // var pos = this.el.getBox(true);
39765 // var maxX = pos.right;
39767 // var maxHeight = 0;
39769 // Roo.each(items, function(item, k){
39773 // item.el.position('absolute');
39775 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39777 // item.el.setWidth(width);
39779 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39781 // item.el.setHeight(height);
39784 // item.el.setXY([x, y], isInstant ? false : true);
39786 // item.el.setXY([maxX - width, y], isInstant ? false : true);
39789 // y = y + height + this.alternativePadWidth;
39791 // maxHeight = maxHeight + height + this.alternativePadWidth;
39795 // this.el.setHeight(maxHeight);
39799 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39801 var pos = this.el.getBox(true);
39806 var maxX = pos.right;
39808 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39810 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39812 Roo.each(queue, function(box, k){
39814 Roo.each(box, function(b, kk){
39816 b.el.position('absolute');
39818 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39819 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39821 if(b.size == 'md-left' || b.size == 'md-right'){
39822 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39823 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39826 b.el.setWidth(width);
39827 b.el.setHeight(height);
39835 var positions = [];
39837 switch (box.length){
39839 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39842 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39845 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39848 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39854 Roo.each(box, function(b,kk){
39856 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39858 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39866 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39868 Roo.each(eItems, function(b,k){
39870 b.size = (k == 0) ? 'sm' : 'xs';
39871 b.x = (k == 0) ? 2 : 1;
39872 b.y = (k == 0) ? 2 : 1;
39874 b.el.position('absolute');
39876 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39878 b.el.setWidth(width);
39880 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39882 b.el.setHeight(height);
39886 var positions = [];
39889 x : maxX - this.unitWidth * 2 - this.gutter,
39894 x : maxX - this.unitWidth,
39895 y : minY + (this.unitWidth + this.gutter) * 2
39899 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39903 Roo.each(eItems, function(b,k){
39905 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39911 getVerticalOneBoxColPositions : function(x, y, box)
39915 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39917 if(box[0].size == 'md-left'){
39921 if(box[0].size == 'md-right'){
39926 x : x + (this.unitWidth + this.gutter) * rand,
39933 getVerticalTwoBoxColPositions : function(x, y, box)
39937 if(box[0].size == 'xs'){
39941 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39945 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39959 x : x + (this.unitWidth + this.gutter) * 2,
39960 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39967 getVerticalThreeBoxColPositions : function(x, y, box)
39971 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39979 x : x + (this.unitWidth + this.gutter) * 1,
39984 x : x + (this.unitWidth + this.gutter) * 2,
39992 if(box[0].size == 'xs' && box[1].size == 'xs'){
40001 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
40005 x : x + (this.unitWidth + this.gutter) * 1,
40019 x : x + (this.unitWidth + this.gutter) * 2,
40024 x : x + (this.unitWidth + this.gutter) * 2,
40025 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40032 getVerticalFourBoxColPositions : function(x, y, box)
40036 if(box[0].size == 'xs'){
40045 y : y + (this.unitHeight + this.gutter) * 1
40050 y : y + (this.unitHeight + this.gutter) * 2
40054 x : x + (this.unitWidth + this.gutter) * 1,
40068 x : x + (this.unitWidth + this.gutter) * 2,
40073 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40074 y : y + (this.unitHeight + this.gutter) * 1
40078 x : x + (this.unitWidth + this.gutter) * 2,
40079 y : y + (this.unitWidth + this.gutter) * 2
40086 getHorizontalOneBoxColPositions : function(maxX, minY, box)
40090 if(box[0].size == 'md-left'){
40092 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40099 if(box[0].size == 'md-right'){
40101 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40102 y : minY + (this.unitWidth + this.gutter) * 1
40108 var rand = Math.floor(Math.random() * (4 - box[0].y));
40111 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40112 y : minY + (this.unitWidth + this.gutter) * rand
40119 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40123 if(box[0].size == 'xs'){
40126 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40131 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40132 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40140 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40145 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40146 y : minY + (this.unitWidth + this.gutter) * 2
40153 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40157 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40160 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40165 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40166 y : minY + (this.unitWidth + this.gutter) * 1
40170 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40171 y : minY + (this.unitWidth + this.gutter) * 2
40178 if(box[0].size == 'xs' && box[1].size == 'xs'){
40181 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40186 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40191 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40192 y : minY + (this.unitWidth + this.gutter) * 1
40200 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40205 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40206 y : minY + (this.unitWidth + this.gutter) * 2
40210 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40211 y : minY + (this.unitWidth + this.gutter) * 2
40218 getHorizontalFourBoxColPositions : function(maxX, minY, box)
40222 if(box[0].size == 'xs'){
40225 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40230 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40235 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),
40240 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40241 y : minY + (this.unitWidth + this.gutter) * 1
40249 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40254 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40255 y : minY + (this.unitWidth + this.gutter) * 2
40259 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40260 y : minY + (this.unitWidth + this.gutter) * 2
40264 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),
40265 y : minY + (this.unitWidth + this.gutter) * 2
40273 * remove a Masonry Brick
40274 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40276 removeBrick : function(brick_id)
40282 for (var i = 0; i<this.bricks.length; i++) {
40283 if (this.bricks[i].id == brick_id) {
40284 this.bricks.splice(i,1);
40285 this.el.dom.removeChild(Roo.get(brick_id).dom);
40292 * adds a Masonry Brick
40293 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40295 addBrick : function(cfg)
40297 var cn = new Roo.bootstrap.MasonryBrick(cfg);
40298 //this.register(cn);
40299 cn.parentId = this.id;
40300 cn.render(this.el);
40305 * register a Masonry Brick
40306 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40309 register : function(brick)
40311 this.bricks.push(brick);
40312 brick.masonryId = this.id;
40316 * clear all the Masonry Brick
40318 clearAll : function()
40321 //this.getChildContainer().dom.innerHTML = "";
40322 this.el.dom.innerHTML = '';
40325 getSelected : function()
40327 if (!this.selectedBrick) {
40331 return this.selectedBrick;
40335 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40339 * register a Masonry Layout
40340 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40343 register : function(layout)
40345 this.groups[layout.id] = layout;
40348 * fetch a Masonry Layout based on the masonry layout ID
40349 * @param {string} the masonry layout to add
40350 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40353 get: function(layout_id) {
40354 if (typeof(this.groups[layout_id]) == 'undefined') {
40357 return this.groups[layout_id] ;
40369 * http://masonry.desandro.com
40371 * The idea is to render all the bricks based on vertical width...
40373 * The original code extends 'outlayer' - we might need to use that....
40379 * @class Roo.bootstrap.LayoutMasonryAuto
40380 * @extends Roo.bootstrap.Component
40381 * Bootstrap Layout Masonry class
40384 * Create a new Element
40385 * @param {Object} config The config object
40388 Roo.bootstrap.LayoutMasonryAuto = function(config){
40389 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40392 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
40395 * @cfg {Boolean} isFitWidth - resize the width..
40397 isFitWidth : false, // options..
40399 * @cfg {Boolean} isOriginLeft = left align?
40401 isOriginLeft : true,
40403 * @cfg {Boolean} isOriginTop = top align?
40405 isOriginTop : false,
40407 * @cfg {Boolean} isLayoutInstant = no animation?
40409 isLayoutInstant : false, // needed?
40411 * @cfg {Boolean} isResizingContainer = not sure if this is used..
40413 isResizingContainer : true,
40415 * @cfg {Number} columnWidth width of the columns
40421 * @cfg {Number} maxCols maximum number of columns
40426 * @cfg {Number} padHeight padding below box..
40432 * @cfg {Boolean} isAutoInitial defalut true
40435 isAutoInitial : true,
40441 initialColumnWidth : 0,
40442 currentSize : null,
40444 colYs : null, // array.
40451 bricks: null, //CompositeElement
40452 cols : 0, // array?
40453 // element : null, // wrapped now this.el
40454 _isLayoutInited : null,
40457 getAutoCreate : function(){
40461 cls: 'blog-masonary-wrapper ' + this.cls,
40463 cls : 'mas-boxes masonary'
40470 getChildContainer: function( )
40472 if (this.boxesEl) {
40473 return this.boxesEl;
40476 this.boxesEl = this.el.select('.mas-boxes').first();
40478 return this.boxesEl;
40482 initEvents : function()
40486 if(this.isAutoInitial){
40487 Roo.log('hook children rendered');
40488 this.on('childrenrendered', function() {
40489 Roo.log('children rendered');
40496 initial : function()
40498 this.reloadItems();
40500 this.currentSize = this.el.getBox(true);
40502 /// was window resize... - let's see if this works..
40503 Roo.EventManager.onWindowResize(this.resize, this);
40505 if(!this.isAutoInitial){
40510 this.layout.defer(500,this);
40513 reloadItems: function()
40515 this.bricks = this.el.select('.masonry-brick', true);
40517 this.bricks.each(function(b) {
40518 //Roo.log(b.getSize());
40519 if (!b.attr('originalwidth')) {
40520 b.attr('originalwidth', b.getSize().width);
40525 Roo.log(this.bricks.elements.length);
40528 resize : function()
40531 var cs = this.el.getBox(true);
40533 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40534 Roo.log("no change in with or X");
40537 this.currentSize = cs;
40541 layout : function()
40544 this._resetLayout();
40545 //this._manageStamps();
40547 // don't animate first layout
40548 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40549 this.layoutItems( isInstant );
40551 // flag for initalized
40552 this._isLayoutInited = true;
40555 layoutItems : function( isInstant )
40557 //var items = this._getItemsForLayout( this.items );
40558 // original code supports filtering layout items.. we just ignore it..
40560 this._layoutItems( this.bricks , isInstant );
40562 this._postLayout();
40564 _layoutItems : function ( items , isInstant)
40566 //this.fireEvent( 'layout', this, items );
40569 if ( !items || !items.elements.length ) {
40570 // no items, emit event with empty array
40575 items.each(function(item) {
40576 Roo.log("layout item");
40578 // get x/y object from method
40579 var position = this._getItemLayoutPosition( item );
40581 position.item = item;
40582 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40583 queue.push( position );
40586 this._processLayoutQueue( queue );
40588 /** Sets position of item in DOM
40589 * @param {Element} item
40590 * @param {Number} x - horizontal position
40591 * @param {Number} y - vertical position
40592 * @param {Boolean} isInstant - disables transitions
40594 _processLayoutQueue : function( queue )
40596 for ( var i=0, len = queue.length; i < len; i++ ) {
40597 var obj = queue[i];
40598 obj.item.position('absolute');
40599 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40605 * Any logic you want to do after each layout,
40606 * i.e. size the container
40608 _postLayout : function()
40610 this.resizeContainer();
40613 resizeContainer : function()
40615 if ( !this.isResizingContainer ) {
40618 var size = this._getContainerSize();
40620 this.el.setSize(size.width,size.height);
40621 this.boxesEl.setSize(size.width,size.height);
40627 _resetLayout : function()
40629 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40630 this.colWidth = this.el.getWidth();
40631 //this.gutter = this.el.getWidth();
40633 this.measureColumns();
40639 this.colYs.push( 0 );
40645 measureColumns : function()
40647 this.getContainerWidth();
40648 // if columnWidth is 0, default to outerWidth of first item
40649 if ( !this.columnWidth ) {
40650 var firstItem = this.bricks.first();
40651 Roo.log(firstItem);
40652 this.columnWidth = this.containerWidth;
40653 if (firstItem && firstItem.attr('originalwidth') ) {
40654 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40656 // columnWidth fall back to item of first element
40657 Roo.log("set column width?");
40658 this.initialColumnWidth = this.columnWidth ;
40660 // if first elem has no width, default to size of container
40665 if (this.initialColumnWidth) {
40666 this.columnWidth = this.initialColumnWidth;
40671 // column width is fixed at the top - however if container width get's smaller we should
40674 // this bit calcs how man columns..
40676 var columnWidth = this.columnWidth += this.gutter;
40678 // calculate columns
40679 var containerWidth = this.containerWidth + this.gutter;
40681 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40682 // fix rounding errors, typically with gutters
40683 var excess = columnWidth - containerWidth % columnWidth;
40686 // if overshoot is less than a pixel, round up, otherwise floor it
40687 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40688 cols = Math[ mathMethod ]( cols );
40689 this.cols = Math.max( cols, 1 );
40690 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40692 // padding positioning..
40693 var totalColWidth = this.cols * this.columnWidth;
40694 var padavail = this.containerWidth - totalColWidth;
40695 // so for 2 columns - we need 3 'pads'
40697 var padNeeded = (1+this.cols) * this.padWidth;
40699 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40701 this.columnWidth += padExtra
40702 //this.padWidth = Math.floor(padavail / ( this.cols));
40704 // adjust colum width so that padding is fixed??
40706 // we have 3 columns ... total = width * 3
40707 // we have X left over... that should be used by
40709 //if (this.expandC) {
40717 getContainerWidth : function()
40719 /* // container is parent if fit width
40720 var container = this.isFitWidth ? this.element.parentNode : this.element;
40721 // check that this.size and size are there
40722 // IE8 triggers resize on body size change, so they might not be
40724 var size = getSize( container ); //FIXME
40725 this.containerWidth = size && size.innerWidth; //FIXME
40728 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
40732 _getItemLayoutPosition : function( item ) // what is item?
40734 // we resize the item to our columnWidth..
40736 item.setWidth(this.columnWidth);
40737 item.autoBoxAdjust = false;
40739 var sz = item.getSize();
40741 // how many columns does this brick span
40742 var remainder = this.containerWidth % this.columnWidth;
40744 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40745 // round if off by 1 pixel, otherwise use ceil
40746 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
40747 colSpan = Math.min( colSpan, this.cols );
40749 // normally this should be '1' as we dont' currently allow multi width columns..
40751 var colGroup = this._getColGroup( colSpan );
40752 // get the minimum Y value from the columns
40753 var minimumY = Math.min.apply( Math, colGroup );
40754 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40756 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
40758 // position the brick
40760 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40761 y: this.currentSize.y + minimumY + this.padHeight
40765 // apply setHeight to necessary columns
40766 var setHeight = minimumY + sz.height + this.padHeight;
40767 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40769 var setSpan = this.cols + 1 - colGroup.length;
40770 for ( var i = 0; i < setSpan; i++ ) {
40771 this.colYs[ shortColIndex + i ] = setHeight ;
40778 * @param {Number} colSpan - number of columns the element spans
40779 * @returns {Array} colGroup
40781 _getColGroup : function( colSpan )
40783 if ( colSpan < 2 ) {
40784 // if brick spans only one column, use all the column Ys
40789 // how many different places could this brick fit horizontally
40790 var groupCount = this.cols + 1 - colSpan;
40791 // for each group potential horizontal position
40792 for ( var i = 0; i < groupCount; i++ ) {
40793 // make an array of colY values for that one group
40794 var groupColYs = this.colYs.slice( i, i + colSpan );
40795 // and get the max value of the array
40796 colGroup[i] = Math.max.apply( Math, groupColYs );
40801 _manageStamp : function( stamp )
40803 var stampSize = stamp.getSize();
40804 var offset = stamp.getBox();
40805 // get the columns that this stamp affects
40806 var firstX = this.isOriginLeft ? offset.x : offset.right;
40807 var lastX = firstX + stampSize.width;
40808 var firstCol = Math.floor( firstX / this.columnWidth );
40809 firstCol = Math.max( 0, firstCol );
40811 var lastCol = Math.floor( lastX / this.columnWidth );
40812 // lastCol should not go over if multiple of columnWidth #425
40813 lastCol -= lastX % this.columnWidth ? 0 : 1;
40814 lastCol = Math.min( this.cols - 1, lastCol );
40816 // set colYs to bottom of the stamp
40817 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40820 for ( var i = firstCol; i <= lastCol; i++ ) {
40821 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40826 _getContainerSize : function()
40828 this.maxY = Math.max.apply( Math, this.colYs );
40833 if ( this.isFitWidth ) {
40834 size.width = this._getContainerFitWidth();
40840 _getContainerFitWidth : function()
40842 var unusedCols = 0;
40843 // count unused columns
40846 if ( this.colYs[i] !== 0 ) {
40851 // fit container to columns that have been used
40852 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40855 needsResizeLayout : function()
40857 var previousWidth = this.containerWidth;
40858 this.getContainerWidth();
40859 return previousWidth !== this.containerWidth;
40874 * @class Roo.bootstrap.MasonryBrick
40875 * @extends Roo.bootstrap.Component
40876 * Bootstrap MasonryBrick class
40879 * Create a new MasonryBrick
40880 * @param {Object} config The config object
40883 Roo.bootstrap.MasonryBrick = function(config){
40885 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40887 Roo.bootstrap.MasonryBrick.register(this);
40893 * When a MasonryBrick is clcik
40894 * @param {Roo.bootstrap.MasonryBrick} this
40895 * @param {Roo.EventObject} e
40901 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40904 * @cfg {String} title
40908 * @cfg {String} html
40912 * @cfg {String} bgimage
40916 * @cfg {String} videourl
40920 * @cfg {String} cls
40924 * @cfg {String} href
40928 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40933 * @cfg {String} placetitle (center|bottom)
40938 * @cfg {Boolean} isFitContainer defalut true
40940 isFitContainer : true,
40943 * @cfg {Boolean} preventDefault defalut false
40945 preventDefault : false,
40948 * @cfg {Boolean} inverse defalut false
40950 maskInverse : false,
40952 getAutoCreate : function()
40954 if(!this.isFitContainer){
40955 return this.getSplitAutoCreate();
40958 var cls = 'masonry-brick masonry-brick-full';
40960 if(this.href.length){
40961 cls += ' masonry-brick-link';
40964 if(this.bgimage.length){
40965 cls += ' masonry-brick-image';
40968 if(this.maskInverse){
40969 cls += ' mask-inverse';
40972 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40973 cls += ' enable-mask';
40977 cls += ' masonry-' + this.size + '-brick';
40980 if(this.placetitle.length){
40982 switch (this.placetitle) {
40984 cls += ' masonry-center-title';
40987 cls += ' masonry-bottom-title';
40994 if(!this.html.length && !this.bgimage.length){
40995 cls += ' masonry-center-title';
40998 if(!this.html.length && this.bgimage.length){
40999 cls += ' masonry-bottom-title';
41004 cls += ' ' + this.cls;
41008 tag: (this.href.length) ? 'a' : 'div',
41013 cls: 'masonry-brick-mask'
41017 cls: 'masonry-brick-paragraph',
41023 if(this.href.length){
41024 cfg.href = this.href;
41027 var cn = cfg.cn[1].cn;
41029 if(this.title.length){
41032 cls: 'masonry-brick-title',
41037 if(this.html.length){
41040 cls: 'masonry-brick-text',
41045 if (!this.title.length && !this.html.length) {
41046 cfg.cn[1].cls += ' hide';
41049 if(this.bgimage.length){
41052 cls: 'masonry-brick-image-view',
41057 if(this.videourl.length){
41058 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41059 // youtube support only?
41062 cls: 'masonry-brick-image-view',
41065 allowfullscreen : true
41073 getSplitAutoCreate : function()
41075 var cls = 'masonry-brick masonry-brick-split';
41077 if(this.href.length){
41078 cls += ' masonry-brick-link';
41081 if(this.bgimage.length){
41082 cls += ' masonry-brick-image';
41086 cls += ' masonry-' + this.size + '-brick';
41089 switch (this.placetitle) {
41091 cls += ' masonry-center-title';
41094 cls += ' masonry-bottom-title';
41097 if(!this.bgimage.length){
41098 cls += ' masonry-center-title';
41101 if(this.bgimage.length){
41102 cls += ' masonry-bottom-title';
41108 cls += ' ' + this.cls;
41112 tag: (this.href.length) ? 'a' : 'div',
41117 cls: 'masonry-brick-split-head',
41121 cls: 'masonry-brick-paragraph',
41128 cls: 'masonry-brick-split-body',
41134 if(this.href.length){
41135 cfg.href = this.href;
41138 if(this.title.length){
41139 cfg.cn[0].cn[0].cn.push({
41141 cls: 'masonry-brick-title',
41146 if(this.html.length){
41147 cfg.cn[1].cn.push({
41149 cls: 'masonry-brick-text',
41154 if(this.bgimage.length){
41155 cfg.cn[0].cn.push({
41157 cls: 'masonry-brick-image-view',
41162 if(this.videourl.length){
41163 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41164 // youtube support only?
41165 cfg.cn[0].cn.cn.push({
41167 cls: 'masonry-brick-image-view',
41170 allowfullscreen : true
41177 initEvents: function()
41179 switch (this.size) {
41212 this.el.on('touchstart', this.onTouchStart, this);
41213 this.el.on('touchmove', this.onTouchMove, this);
41214 this.el.on('touchend', this.onTouchEnd, this);
41215 this.el.on('contextmenu', this.onContextMenu, this);
41217 this.el.on('mouseenter' ,this.enter, this);
41218 this.el.on('mouseleave', this.leave, this);
41219 this.el.on('click', this.onClick, this);
41222 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41223 this.parent().bricks.push(this);
41228 onClick: function(e, el)
41230 var time = this.endTimer - this.startTimer;
41231 // Roo.log(e.preventDefault());
41234 e.preventDefault();
41239 if(!this.preventDefault){
41243 e.preventDefault();
41245 if (this.activeClass != '') {
41246 this.selectBrick();
41249 this.fireEvent('click', this, e);
41252 enter: 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.9, true);
41265 leave: function(e, el)
41267 e.preventDefault();
41269 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41273 if(this.bgimage.length && this.html.length){
41274 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41278 onTouchStart: function(e, el)
41280 // e.preventDefault();
41282 this.touchmoved = false;
41284 if(!this.isFitContainer){
41288 if(!this.bgimage.length || !this.html.length){
41292 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41294 this.timer = new Date().getTime();
41298 onTouchMove: function(e, el)
41300 this.touchmoved = true;
41303 onContextMenu : function(e,el)
41305 e.preventDefault();
41306 e.stopPropagation();
41310 onTouchEnd: function(e, el)
41312 // e.preventDefault();
41314 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41321 if(!this.bgimage.length || !this.html.length){
41323 if(this.href.length){
41324 window.location.href = this.href;
41330 if(!this.isFitContainer){
41334 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41336 window.location.href = this.href;
41339 //selection on single brick only
41340 selectBrick : function() {
41342 if (!this.parentId) {
41346 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41347 var index = m.selectedBrick.indexOf(this.id);
41350 m.selectedBrick.splice(index,1);
41351 this.el.removeClass(this.activeClass);
41355 for(var i = 0; i < m.selectedBrick.length; i++) {
41356 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41357 b.el.removeClass(b.activeClass);
41360 m.selectedBrick = [];
41362 m.selectedBrick.push(this.id);
41363 this.el.addClass(this.activeClass);
41367 isSelected : function(){
41368 return this.el.hasClass(this.activeClass);
41373 Roo.apply(Roo.bootstrap.MasonryBrick, {
41376 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41378 * register a Masonry Brick
41379 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41382 register : function(brick)
41384 //this.groups[brick.id] = brick;
41385 this.groups.add(brick.id, brick);
41388 * fetch a masonry brick based on the masonry brick ID
41389 * @param {string} the masonry brick to add
41390 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41393 get: function(brick_id)
41395 // if (typeof(this.groups[brick_id]) == 'undefined') {
41398 // return this.groups[brick_id] ;
41400 if(this.groups.key(brick_id)) {
41401 return this.groups.key(brick_id);
41419 * @class Roo.bootstrap.Brick
41420 * @extends Roo.bootstrap.Component
41421 * Bootstrap Brick class
41424 * Create a new Brick
41425 * @param {Object} config The config object
41428 Roo.bootstrap.Brick = function(config){
41429 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41435 * When a Brick is click
41436 * @param {Roo.bootstrap.Brick} this
41437 * @param {Roo.EventObject} e
41443 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
41446 * @cfg {String} title
41450 * @cfg {String} html
41454 * @cfg {String} bgimage
41458 * @cfg {String} cls
41462 * @cfg {String} href
41466 * @cfg {String} video
41470 * @cfg {Boolean} square
41474 getAutoCreate : function()
41476 var cls = 'roo-brick';
41478 if(this.href.length){
41479 cls += ' roo-brick-link';
41482 if(this.bgimage.length){
41483 cls += ' roo-brick-image';
41486 if(!this.html.length && !this.bgimage.length){
41487 cls += ' roo-brick-center-title';
41490 if(!this.html.length && this.bgimage.length){
41491 cls += ' roo-brick-bottom-title';
41495 cls += ' ' + this.cls;
41499 tag: (this.href.length) ? 'a' : 'div',
41504 cls: 'roo-brick-paragraph',
41510 if(this.href.length){
41511 cfg.href = this.href;
41514 var cn = cfg.cn[0].cn;
41516 if(this.title.length){
41519 cls: 'roo-brick-title',
41524 if(this.html.length){
41527 cls: 'roo-brick-text',
41534 if(this.bgimage.length){
41537 cls: 'roo-brick-image-view',
41545 initEvents: function()
41547 if(this.title.length || this.html.length){
41548 this.el.on('mouseenter' ,this.enter, this);
41549 this.el.on('mouseleave', this.leave, this);
41552 Roo.EventManager.onWindowResize(this.resize, this);
41554 if(this.bgimage.length){
41555 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41556 this.imageEl.on('load', this.onImageLoad, this);
41563 onImageLoad : function()
41568 resize : function()
41570 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41572 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41574 if(this.bgimage.length){
41575 var image = this.el.select('.roo-brick-image-view', true).first();
41577 image.setWidth(paragraph.getWidth());
41580 image.setHeight(paragraph.getWidth());
41583 this.el.setHeight(image.getHeight());
41584 paragraph.setHeight(image.getHeight());
41590 enter: function(e, el)
41592 e.preventDefault();
41594 if(this.bgimage.length){
41595 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41596 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41600 leave: function(e, el)
41602 e.preventDefault();
41604 if(this.bgimage.length){
41605 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41606 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41621 * @class Roo.bootstrap.form.NumberField
41622 * @extends Roo.bootstrap.form.Input
41623 * Bootstrap NumberField class
41629 * Create a new NumberField
41630 * @param {Object} config The config object
41633 Roo.bootstrap.form.NumberField = function(config){
41634 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41637 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41640 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41642 allowDecimals : true,
41644 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41646 decimalSeparator : ".",
41648 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41650 decimalPrecision : 2,
41652 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41654 allowNegative : true,
41657 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41661 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41663 minValue : Number.NEGATIVE_INFINITY,
41665 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41667 maxValue : Number.MAX_VALUE,
41669 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41671 minText : "The minimum value for this field is {0}",
41673 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41675 maxText : "The maximum value for this field is {0}",
41677 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41678 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41680 nanText : "{0} is not a valid number",
41682 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41684 thousandsDelimiter : false,
41686 * @cfg {String} valueAlign alignment of value
41688 valueAlign : "left",
41690 getAutoCreate : function()
41692 var hiddenInput = {
41696 cls: 'hidden-number-input'
41700 hiddenInput.name = this.name;
41705 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41707 this.name = hiddenInput.name;
41709 if(cfg.cn.length > 0) {
41710 cfg.cn.push(hiddenInput);
41717 initEvents : function()
41719 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41721 var allowed = "0123456789";
41723 if(this.allowDecimals){
41724 allowed += this.decimalSeparator;
41727 if(this.allowNegative){
41731 if(this.thousandsDelimiter) {
41735 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41737 var keyPress = function(e){
41739 var k = e.getKey();
41741 var c = e.getCharCode();
41744 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41745 allowed.indexOf(String.fromCharCode(c)) === -1
41751 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41755 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41760 this.el.on("keypress", keyPress, this);
41763 validateValue : function(value)
41766 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41770 var num = this.parseValue(value);
41773 this.markInvalid(String.format(this.nanText, value));
41777 if(num < this.minValue){
41778 this.markInvalid(String.format(this.minText, this.minValue));
41782 if(num > this.maxValue){
41783 this.markInvalid(String.format(this.maxText, this.maxValue));
41790 getValue : function()
41792 var v = this.hiddenEl().getValue();
41794 return this.fixPrecision(this.parseValue(v));
41797 parseValue : function(value)
41799 if(this.thousandsDelimiter) {
41801 r = new RegExp(",", "g");
41802 value = value.replace(r, "");
41805 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41806 return isNaN(value) ? '' : value;
41809 fixPrecision : function(value)
41811 if(this.thousandsDelimiter) {
41813 r = new RegExp(",", "g");
41814 value = value.replace(r, "");
41817 var nan = isNaN(value);
41819 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41820 return nan ? '' : value;
41822 return parseFloat(value).toFixed(this.decimalPrecision);
41825 setValue : function(v)
41827 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41833 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41835 this.inputEl().dom.value = (v == '') ? '' :
41836 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41838 if(!this.allowZero && v === '0') {
41839 this.hiddenEl().dom.value = '';
41840 this.inputEl().dom.value = '';
41847 decimalPrecisionFcn : function(v)
41849 return Math.floor(v);
41852 beforeBlur : function()
41854 var v = this.parseValue(this.getRawValue());
41856 if(v || v === 0 || v === ''){
41861 hiddenEl : function()
41863 return this.el.select('input.hidden-number-input',true).first();
41875 * @class Roo.bootstrap.DocumentSlider
41876 * @extends Roo.bootstrap.Component
41877 * Bootstrap DocumentSlider class
41880 * Create a new DocumentViewer
41881 * @param {Object} config The config object
41884 Roo.bootstrap.DocumentSlider = function(config){
41885 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41892 * Fire after initEvent
41893 * @param {Roo.bootstrap.DocumentSlider} this
41898 * Fire after update
41899 * @param {Roo.bootstrap.DocumentSlider} this
41905 * @param {Roo.bootstrap.DocumentSlider} this
41911 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41917 getAutoCreate : function()
41921 cls : 'roo-document-slider',
41925 cls : 'roo-document-slider-header',
41929 cls : 'roo-document-slider-header-title'
41935 cls : 'roo-document-slider-body',
41939 cls : 'roo-document-slider-prev',
41943 cls : 'fa fa-chevron-left'
41949 cls : 'roo-document-slider-thumb',
41953 cls : 'roo-document-slider-image'
41959 cls : 'roo-document-slider-next',
41963 cls : 'fa fa-chevron-right'
41975 initEvents : function()
41977 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41978 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41980 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41981 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41983 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41984 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41986 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41987 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41989 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41990 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41992 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41993 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41995 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41996 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41998 this.thumbEl.on('click', this.onClick, this);
42000 this.prevIndicator.on('click', this.prev, this);
42002 this.nextIndicator.on('click', this.next, this);
42006 initial : function()
42008 if(this.files.length){
42009 this.indicator = 1;
42013 this.fireEvent('initial', this);
42016 update : function()
42018 this.imageEl.attr('src', this.files[this.indicator - 1]);
42020 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42022 this.prevIndicator.show();
42024 if(this.indicator == 1){
42025 this.prevIndicator.hide();
42028 this.nextIndicator.show();
42030 if(this.indicator == this.files.length){
42031 this.nextIndicator.hide();
42034 this.thumbEl.scrollTo('top');
42036 this.fireEvent('update', this);
42039 onClick : function(e)
42041 e.preventDefault();
42043 this.fireEvent('click', this);
42048 e.preventDefault();
42050 this.indicator = Math.max(1, this.indicator - 1);
42057 e.preventDefault();
42059 this.indicator = Math.min(this.files.length, this.indicator + 1);
42073 * @class Roo.bootstrap.form.RadioSet
42074 * @extends Roo.bootstrap.form.Input
42075 * @children Roo.bootstrap.form.Radio
42076 * Bootstrap RadioSet class
42077 * @cfg {String} indicatorpos (left|right) default left
42078 * @cfg {Boolean} inline (true|false) inline the element (default true)
42079 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42081 * Create a new RadioSet
42082 * @param {Object} config The config object
42085 Roo.bootstrap.form.RadioSet = function(config){
42087 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42091 Roo.bootstrap.form.RadioSet.register(this);
42096 * Fires when the element is checked or unchecked.
42097 * @param {Roo.bootstrap.form.RadioSet} this This radio
42098 * @param {Roo.bootstrap.form.Radio} item The checked item
42103 * Fires when the element is click.
42104 * @param {Roo.bootstrap.form.RadioSet} this This radio set
42105 * @param {Roo.bootstrap.form.Radio} item The checked item
42106 * @param {Roo.EventObject} e The event object
42113 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
42121 indicatorpos : 'left',
42123 getAutoCreate : function()
42127 cls : 'roo-radio-set-label',
42131 html : this.fieldLabel
42135 if (Roo.bootstrap.version == 3) {
42138 if(this.indicatorpos == 'left'){
42141 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42142 tooltip : 'This field is required'
42147 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42148 tooltip : 'This field is required'
42154 cls : 'roo-radio-set-items'
42157 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42159 if (align === 'left' && this.fieldLabel.length) {
42162 cls : "roo-radio-set-right",
42168 if(this.labelWidth > 12){
42169 label.style = "width: " + this.labelWidth + 'px';
42172 if(this.labelWidth < 13 && this.labelmd == 0){
42173 this.labelmd = this.labelWidth;
42176 if(this.labellg > 0){
42177 label.cls += ' col-lg-' + this.labellg;
42178 items.cls += ' col-lg-' + (12 - this.labellg);
42181 if(this.labelmd > 0){
42182 label.cls += ' col-md-' + this.labelmd;
42183 items.cls += ' col-md-' + (12 - this.labelmd);
42186 if(this.labelsm > 0){
42187 label.cls += ' col-sm-' + this.labelsm;
42188 items.cls += ' col-sm-' + (12 - this.labelsm);
42191 if(this.labelxs > 0){
42192 label.cls += ' col-xs-' + this.labelxs;
42193 items.cls += ' col-xs-' + (12 - this.labelxs);
42199 cls : 'roo-radio-set',
42203 cls : 'roo-radio-set-input',
42206 value : this.value ? this.value : ''
42213 if(this.weight.length){
42214 cfg.cls += ' roo-radio-' + this.weight;
42218 cfg.cls += ' roo-radio-set-inline';
42222 ['xs','sm','md','lg'].map(function(size){
42223 if (settings[size]) {
42224 cfg.cls += ' col-' + size + '-' + settings[size];
42232 initEvents : function()
42234 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42235 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42237 if(!this.fieldLabel.length){
42238 this.labelEl.hide();
42241 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42242 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42244 this.indicator = this.indicatorEl();
42246 if(this.indicator){
42247 this.indicator.addClass('invisible');
42250 this.originalValue = this.getValue();
42254 inputEl: function ()
42256 return this.el.select('.roo-radio-set-input', true).first();
42259 getChildContainer : function()
42261 return this.itemsEl;
42264 register : function(item)
42266 this.radioes.push(item);
42270 validate : function()
42272 if(this.getVisibilityEl().hasClass('hidden')){
42278 Roo.each(this.radioes, function(i){
42287 if(this.allowBlank) {
42291 if(this.disabled || valid){
42296 this.markInvalid();
42301 markValid : function()
42303 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42304 this.indicatorEl().removeClass('visible');
42305 this.indicatorEl().addClass('invisible');
42309 if (Roo.bootstrap.version == 3) {
42310 this.el.removeClass([this.invalidClass, this.validClass]);
42311 this.el.addClass(this.validClass);
42313 this.el.removeClass(['is-invalid','is-valid']);
42314 this.el.addClass(['is-valid']);
42316 this.fireEvent('valid', this);
42319 markInvalid : function(msg)
42321 if(this.allowBlank || this.disabled){
42325 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42326 this.indicatorEl().removeClass('invisible');
42327 this.indicatorEl().addClass('visible');
42329 if (Roo.bootstrap.version == 3) {
42330 this.el.removeClass([this.invalidClass, this.validClass]);
42331 this.el.addClass(this.invalidClass);
42333 this.el.removeClass(['is-invalid','is-valid']);
42334 this.el.addClass(['is-invalid']);
42337 this.fireEvent('invalid', this, msg);
42341 setValue : function(v, suppressEvent)
42343 if(this.value === v){
42350 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42353 Roo.each(this.radioes, function(i){
42355 i.el.removeClass('checked');
42358 Roo.each(this.radioes, function(i){
42360 if(i.value === v || i.value.toString() === v.toString()){
42362 i.el.addClass('checked');
42364 if(suppressEvent !== true){
42365 this.fireEvent('check', this, i);
42376 clearInvalid : function(){
42378 if(!this.el || this.preventMark){
42382 this.el.removeClass([this.invalidClass]);
42384 this.fireEvent('valid', this);
42389 Roo.apply(Roo.bootstrap.form.RadioSet, {
42393 register : function(set)
42395 this.groups[set.name] = set;
42398 get: function(name)
42400 if (typeof(this.groups[name]) == 'undefined') {
42404 return this.groups[name] ;
42410 * Ext JS Library 1.1.1
42411 * Copyright(c) 2006-2007, Ext JS, LLC.
42413 * Originally Released Under LGPL - original licence link has changed is not relivant.
42416 * <script type="text/javascript">
42421 * @class Roo.bootstrap.SplitBar
42422 * @extends Roo.util.Observable
42423 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42427 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42428 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42429 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42430 split.minSize = 100;
42431 split.maxSize = 600;
42432 split.animate = true;
42433 split.on('moved', splitterMoved);
42436 * Create a new SplitBar
42437 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
42438 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
42439 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42440 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
42441 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42442 position of the SplitBar).
42444 Roo.bootstrap.SplitBar = function(cfg){
42449 // dragElement : elm
42450 // resizingElement: el,
42452 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42453 // placement : Roo.bootstrap.SplitBar.LEFT ,
42454 // existingProxy ???
42457 this.el = Roo.get(cfg.dragElement, true);
42458 this.el.dom.unselectable = "on";
42460 this.resizingEl = Roo.get(cfg.resizingElement, true);
42464 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42465 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42468 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42471 * The minimum size of the resizing element. (Defaults to 0)
42477 * The maximum size of the resizing element. (Defaults to 2000)
42480 this.maxSize = 2000;
42483 * Whether to animate the transition to the new size
42486 this.animate = false;
42489 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42492 this.useShim = false;
42497 if(!cfg.existingProxy){
42499 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42501 this.proxy = Roo.get(cfg.existingProxy).dom;
42504 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42507 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42510 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42513 this.dragSpecs = {};
42516 * @private The adapter to use to positon and resize elements
42518 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42519 this.adapter.init(this);
42521 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42523 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42524 this.el.addClass("roo-splitbar-h");
42527 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42528 this.el.addClass("roo-splitbar-v");
42534 * Fires when the splitter is moved (alias for {@link #event-moved})
42535 * @param {Roo.bootstrap.SplitBar} this
42536 * @param {Number} newSize the new width or height
42541 * Fires when the splitter is moved
42542 * @param {Roo.bootstrap.SplitBar} this
42543 * @param {Number} newSize the new width or height
42547 * @event beforeresize
42548 * Fires before the splitter is dragged
42549 * @param {Roo.bootstrap.SplitBar} this
42551 "beforeresize" : true,
42553 "beforeapply" : true
42556 Roo.util.Observable.call(this);
42559 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42560 onStartProxyDrag : function(x, y){
42561 this.fireEvent("beforeresize", this);
42563 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
42565 o.enableDisplayMode("block");
42566 // all splitbars share the same overlay
42567 Roo.bootstrap.SplitBar.prototype.overlay = o;
42569 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42570 this.overlay.show();
42571 Roo.get(this.proxy).setDisplayed("block");
42572 var size = this.adapter.getElementSize(this);
42573 this.activeMinSize = this.getMinimumSize();;
42574 this.activeMaxSize = this.getMaximumSize();;
42575 var c1 = size - this.activeMinSize;
42576 var c2 = Math.max(this.activeMaxSize - size, 0);
42577 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42578 this.dd.resetConstraints();
42579 this.dd.setXConstraint(
42580 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
42581 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42583 this.dd.setYConstraint(0, 0);
42585 this.dd.resetConstraints();
42586 this.dd.setXConstraint(0, 0);
42587 this.dd.setYConstraint(
42588 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
42589 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42592 this.dragSpecs.startSize = size;
42593 this.dragSpecs.startPoint = [x, y];
42594 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42598 * @private Called after the drag operation by the DDProxy
42600 onEndProxyDrag : function(e){
42601 Roo.get(this.proxy).setDisplayed(false);
42602 var endPoint = Roo.lib.Event.getXY(e);
42604 this.overlay.hide();
42607 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42608 newSize = this.dragSpecs.startSize +
42609 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42610 endPoint[0] - this.dragSpecs.startPoint[0] :
42611 this.dragSpecs.startPoint[0] - endPoint[0]
42614 newSize = this.dragSpecs.startSize +
42615 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42616 endPoint[1] - this.dragSpecs.startPoint[1] :
42617 this.dragSpecs.startPoint[1] - endPoint[1]
42620 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42621 if(newSize != this.dragSpecs.startSize){
42622 if(this.fireEvent('beforeapply', this, newSize) !== false){
42623 this.adapter.setElementSize(this, newSize);
42624 this.fireEvent("moved", this, newSize);
42625 this.fireEvent("resize", this, newSize);
42631 * Get the adapter this SplitBar uses
42632 * @return The adapter object
42634 getAdapter : function(){
42635 return this.adapter;
42639 * Set the adapter this SplitBar uses
42640 * @param {Object} adapter A SplitBar adapter object
42642 setAdapter : function(adapter){
42643 this.adapter = adapter;
42644 this.adapter.init(this);
42648 * Gets the minimum size for the resizing element
42649 * @return {Number} The minimum size
42651 getMinimumSize : function(){
42652 return this.minSize;
42656 * Sets the minimum size for the resizing element
42657 * @param {Number} minSize The minimum size
42659 setMinimumSize : function(minSize){
42660 this.minSize = minSize;
42664 * Gets the maximum size for the resizing element
42665 * @return {Number} The maximum size
42667 getMaximumSize : function(){
42668 return this.maxSize;
42672 * Sets the maximum size for the resizing element
42673 * @param {Number} maxSize The maximum size
42675 setMaximumSize : function(maxSize){
42676 this.maxSize = maxSize;
42680 * Sets the initialize size for the resizing element
42681 * @param {Number} size The initial size
42683 setCurrentSize : function(size){
42684 var oldAnimate = this.animate;
42685 this.animate = false;
42686 this.adapter.setElementSize(this, size);
42687 this.animate = oldAnimate;
42691 * Destroy this splitbar.
42692 * @param {Boolean} removeEl True to remove the element
42694 destroy : function(removeEl){
42696 this.shim.remove();
42699 this.proxy.parentNode.removeChild(this.proxy);
42707 * @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.
42709 Roo.bootstrap.SplitBar.createProxy = function(dir){
42710 var proxy = new Roo.Element(document.createElement("div"));
42711 proxy.unselectable();
42712 var cls = 'roo-splitbar-proxy';
42713 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42714 document.body.appendChild(proxy.dom);
42719 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42720 * Default Adapter. It assumes the splitter and resizing element are not positioned
42721 * elements and only gets/sets the width of the element. Generally used for table based layouts.
42723 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42726 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42727 // do nothing for now
42728 init : function(s){
42732 * Called before drag operations to get the current size of the resizing element.
42733 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42735 getElementSize : function(s){
42736 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42737 return s.resizingEl.getWidth();
42739 return s.resizingEl.getHeight();
42744 * Called after drag operations to set the size of the resizing element.
42745 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42746 * @param {Number} newSize The new size to set
42747 * @param {Function} onComplete A function to be invoked when resizing is complete
42749 setElementSize : function(s, newSize, onComplete){
42750 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42752 s.resizingEl.setWidth(newSize);
42754 onComplete(s, newSize);
42757 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42762 s.resizingEl.setHeight(newSize);
42764 onComplete(s, newSize);
42767 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42774 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42775 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42776 * Adapter that moves the splitter element to align with the resized sizing element.
42777 * Used with an absolute positioned SplitBar.
42778 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42779 * document.body, make sure you assign an id to the body element.
42781 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42782 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42783 this.container = Roo.get(container);
42786 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42787 init : function(s){
42788 this.basic.init(s);
42791 getElementSize : function(s){
42792 return this.basic.getElementSize(s);
42795 setElementSize : function(s, newSize, onComplete){
42796 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42799 moveSplitter : function(s){
42800 var yes = Roo.bootstrap.SplitBar;
42801 switch(s.placement){
42803 s.el.setX(s.resizingEl.getRight());
42806 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42809 s.el.setY(s.resizingEl.getBottom());
42812 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42819 * Orientation constant - Create a vertical SplitBar
42823 Roo.bootstrap.SplitBar.VERTICAL = 1;
42826 * Orientation constant - Create a horizontal SplitBar
42830 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42833 * Placement constant - The resizing element is to the left of the splitter element
42837 Roo.bootstrap.SplitBar.LEFT = 1;
42840 * Placement constant - The resizing element is to the right of the splitter element
42844 Roo.bootstrap.SplitBar.RIGHT = 2;
42847 * Placement constant - The resizing element is positioned above the splitter element
42851 Roo.bootstrap.SplitBar.TOP = 3;
42854 * Placement constant - The resizing element is positioned under splitter element
42858 Roo.bootstrap.SplitBar.BOTTOM = 4;
42861 * Ext JS Library 1.1.1
42862 * Copyright(c) 2006-2007, Ext JS, LLC.
42864 * Originally Released Under LGPL - original licence link has changed is not relivant.
42867 * <script type="text/javascript">
42871 * @class Roo.bootstrap.layout.Manager
42872 * @extends Roo.bootstrap.Component
42874 * Base class for layout managers.
42876 Roo.bootstrap.layout.Manager = function(config)
42878 this.monitorWindowResize = true; // do this before we apply configuration.
42880 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42886 /** false to disable window resize monitoring @type Boolean */
42892 * Fires when a layout is performed.
42893 * @param {Roo.LayoutManager} this
42897 * @event regionresized
42898 * Fires when the user resizes a region.
42899 * @param {Roo.LayoutRegion} region The resized region
42900 * @param {Number} newSize The new size (width for east/west, height for north/south)
42902 "regionresized" : true,
42904 * @event regioncollapsed
42905 * Fires when a region is collapsed.
42906 * @param {Roo.LayoutRegion} region The collapsed region
42908 "regioncollapsed" : true,
42910 * @event regionexpanded
42911 * Fires when a region is expanded.
42912 * @param {Roo.LayoutRegion} region The expanded region
42914 "regionexpanded" : true
42916 this.updating = false;
42919 this.el = Roo.get(config.el);
42925 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42930 monitorWindowResize : true,
42936 onRender : function(ct, position)
42939 this.el = Roo.get(ct);
42942 //this.fireEvent('render',this);
42946 initEvents: function()
42950 // ie scrollbar fix
42951 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42952 document.body.scroll = "no";
42953 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42954 this.el.position('relative');
42956 this.id = this.el.id;
42957 this.el.addClass("roo-layout-container");
42958 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42959 if(this.el.dom != document.body ) {
42960 this.el.on('resize', this.layout,this);
42961 this.el.on('show', this.layout,this);
42967 * Returns true if this layout is currently being updated
42968 * @return {Boolean}
42970 isUpdating : function(){
42971 return this.updating;
42975 * Suspend the LayoutManager from doing auto-layouts while
42976 * making multiple add or remove calls
42978 beginUpdate : function(){
42979 this.updating = true;
42983 * Restore auto-layouts and optionally disable the manager from performing a layout
42984 * @param {Boolean} noLayout true to disable a layout update
42986 endUpdate : function(noLayout){
42987 this.updating = false;
42993 layout: function(){
42997 onRegionResized : function(region, newSize){
42998 this.fireEvent("regionresized", region, newSize);
43002 onRegionCollapsed : function(region){
43003 this.fireEvent("regioncollapsed", region);
43006 onRegionExpanded : function(region){
43007 this.fireEvent("regionexpanded", region);
43011 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43012 * performs box-model adjustments.
43013 * @return {Object} The size as an object {width: (the width), height: (the height)}
43015 getViewSize : function()
43018 if(this.el.dom != document.body){
43019 size = this.el.getSize();
43021 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43023 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43024 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43029 * Returns the Element this layout is bound to.
43030 * @return {Roo.Element}
43032 getEl : function(){
43037 * Returns the specified region.
43038 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43039 * @return {Roo.LayoutRegion}
43041 getRegion : function(target){
43042 return this.regions[target.toLowerCase()];
43045 onWindowResize : function(){
43046 if(this.monitorWindowResize){
43053 * Ext JS Library 1.1.1
43054 * Copyright(c) 2006-2007, Ext JS, LLC.
43056 * Originally Released Under LGPL - original licence link has changed is not relivant.
43059 * <script type="text/javascript">
43062 * @class Roo.bootstrap.layout.Border
43063 * @extends Roo.bootstrap.layout.Manager
43064 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43065 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43066 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43067 * please see: examples/bootstrap/nested.html<br><br>
43069 <b>The container the layout is rendered into can be either the body element or any other element.
43070 If it is not the body element, the container needs to either be an absolute positioned element,
43071 or you will need to add "position:relative" to the css of the container. You will also need to specify
43072 the container size if it is not the body element.</b>
43075 * Create a new Border
43076 * @param {Object} config Configuration options
43078 Roo.bootstrap.layout.Border = function(config){
43079 config = config || {};
43080 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43084 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43085 if(config[region]){
43086 config[region].region = region;
43087 this.addRegion(config[region]);
43093 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
43095 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43098 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43101 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43104 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43107 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43110 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43116 parent : false, // this might point to a 'nest' or a ???
43119 * Creates and adds a new region if it doesn't already exist.
43120 * @param {String} target The target region key (north, south, east, west or center).
43121 * @param {Object} config The regions config object
43122 * @return {BorderLayoutRegion} The new region
43124 addRegion : function(config)
43126 if(!this.regions[config.region]){
43127 var r = this.factory(config);
43128 this.bindRegion(r);
43130 return this.regions[config.region];
43134 bindRegion : function(r){
43135 this.regions[r.config.region] = r;
43137 r.on("visibilitychange", this.layout, this);
43138 r.on("paneladded", this.layout, this);
43139 r.on("panelremoved", this.layout, this);
43140 r.on("invalidated", this.layout, this);
43141 r.on("resized", this.onRegionResized, this);
43142 r.on("collapsed", this.onRegionCollapsed, this);
43143 r.on("expanded", this.onRegionExpanded, this);
43147 * Performs a layout update.
43149 layout : function()
43151 if(this.updating) {
43155 // render all the rebions if they have not been done alreayd?
43156 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43157 if(this.regions[region] && !this.regions[region].bodyEl){
43158 this.regions[region].onRender(this.el)
43162 var size = this.getViewSize();
43163 var w = size.width;
43164 var h = size.height;
43169 //var x = 0, y = 0;
43171 var rs = this.regions;
43172 var north = rs["north"];
43173 var south = rs["south"];
43174 var west = rs["west"];
43175 var east = rs["east"];
43176 var center = rs["center"];
43177 //if(this.hideOnLayout){ // not supported anymore
43178 //c.el.setStyle("display", "none");
43180 if(north && north.isVisible()){
43181 var b = north.getBox();
43182 var m = north.getMargins();
43183 b.width = w - (m.left+m.right);
43186 centerY = b.height + b.y + m.bottom;
43187 centerH -= centerY;
43188 north.updateBox(this.safeBox(b));
43190 if(south && south.isVisible()){
43191 var b = south.getBox();
43192 var m = south.getMargins();
43193 b.width = w - (m.left+m.right);
43195 var totalHeight = (b.height + m.top + m.bottom);
43196 b.y = h - totalHeight + m.top;
43197 centerH -= totalHeight;
43198 south.updateBox(this.safeBox(b));
43200 if(west && west.isVisible()){
43201 var b = west.getBox();
43202 var m = west.getMargins();
43203 b.height = centerH - (m.top+m.bottom);
43205 b.y = centerY + m.top;
43206 var totalWidth = (b.width + m.left + m.right);
43207 centerX += totalWidth;
43208 centerW -= totalWidth;
43209 west.updateBox(this.safeBox(b));
43211 if(east && east.isVisible()){
43212 var b = east.getBox();
43213 var m = east.getMargins();
43214 b.height = centerH - (m.top+m.bottom);
43215 var totalWidth = (b.width + m.left + m.right);
43216 b.x = w - totalWidth + m.left;
43217 b.y = centerY + m.top;
43218 centerW -= totalWidth;
43219 east.updateBox(this.safeBox(b));
43222 var m = center.getMargins();
43224 x: centerX + m.left,
43225 y: centerY + m.top,
43226 width: centerW - (m.left+m.right),
43227 height: centerH - (m.top+m.bottom)
43229 //if(this.hideOnLayout){
43230 //center.el.setStyle("display", "block");
43232 center.updateBox(this.safeBox(centerBox));
43235 this.fireEvent("layout", this);
43239 safeBox : function(box){
43240 box.width = Math.max(0, box.width);
43241 box.height = Math.max(0, box.height);
43246 * Adds a ContentPanel (or subclass) to this layout.
43247 * @param {String} target The target region key (north, south, east, west or center).
43248 * @param {Roo.ContentPanel} panel The panel to add
43249 * @return {Roo.ContentPanel} The added panel
43251 add : function(target, panel){
43253 target = target.toLowerCase();
43254 return this.regions[target].add(panel);
43258 * Remove a ContentPanel (or subclass) to this layout.
43259 * @param {String} target The target region key (north, south, east, west or center).
43260 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43261 * @return {Roo.ContentPanel} The removed panel
43263 remove : function(target, panel){
43264 target = target.toLowerCase();
43265 return this.regions[target].remove(panel);
43269 * Searches all regions for a panel with the specified id
43270 * @param {String} panelId
43271 * @return {Roo.ContentPanel} The panel or null if it wasn't found
43273 findPanel : function(panelId){
43274 var rs = this.regions;
43275 for(var target in rs){
43276 if(typeof rs[target] != "function"){
43277 var p = rs[target].getPanel(panelId);
43287 * Searches all regions for a panel with the specified id and activates (shows) it.
43288 * @param {String/ContentPanel} panelId The panels id or the panel itself
43289 * @return {Roo.ContentPanel} The shown panel or null
43291 showPanel : function(panelId) {
43292 var rs = this.regions;
43293 for(var target in rs){
43294 var r = rs[target];
43295 if(typeof r != "function"){
43296 if(r.hasPanel(panelId)){
43297 return r.showPanel(panelId);
43305 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43306 * @param {Roo.state.Provider} provider (optional) An alternate state provider
43309 restoreState : function(provider){
43311 provider = Roo.state.Manager;
43313 var sm = new Roo.LayoutStateManager();
43314 sm.init(this, provider);
43320 * Adds a xtype elements to the layout.
43324 xtype : 'ContentPanel',
43331 xtype : 'NestedLayoutPanel',
43337 items : [ ... list of content panels or nested layout panels.. ]
43341 * @param {Object} cfg Xtype definition of item to add.
43343 addxtype : function(cfg)
43345 // basically accepts a pannel...
43346 // can accept a layout region..!?!?
43347 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43350 // theory? children can only be panels??
43352 //if (!cfg.xtype.match(/Panel$/)) {
43357 if (typeof(cfg.region) == 'undefined') {
43358 Roo.log("Failed to add Panel, region was not set");
43362 var region = cfg.region;
43368 xitems = cfg.items;
43373 if ( region == 'center') {
43374 Roo.log("Center: " + cfg.title);
43380 case 'Content': // ContentPanel (el, cfg)
43381 case 'Scroll': // ContentPanel (el, cfg)
43383 cfg.autoCreate = cfg.autoCreate || true;
43384 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43386 // var el = this.el.createChild();
43387 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43390 this.add(region, ret);
43394 case 'TreePanel': // our new panel!
43395 cfg.el = this.el.createChild();
43396 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43397 this.add(region, ret);
43402 // create a new Layout (which is a Border Layout...
43404 var clayout = cfg.layout;
43405 clayout.el = this.el.createChild();
43406 clayout.items = clayout.items || [];
43410 // replace this exitems with the clayout ones..
43411 xitems = clayout.items;
43413 // force background off if it's in center...
43414 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43415 cfg.background = false;
43417 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
43420 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43421 //console.log('adding nested layout panel ' + cfg.toSource());
43422 this.add(region, ret);
43423 nb = {}; /// find first...
43428 // needs grid and region
43430 //var el = this.getRegion(region).el.createChild();
43432 *var el = this.el.createChild();
43433 // create the grid first...
43434 cfg.grid.container = el;
43435 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43438 if (region == 'center' && this.active ) {
43439 cfg.background = false;
43442 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43444 this.add(region, ret);
43446 if (cfg.background) {
43447 // render grid on panel activation (if panel background)
43448 ret.on('activate', function(gp) {
43449 if (!gp.grid.rendered) {
43450 // gp.grid.render(el);
43454 // cfg.grid.render(el);
43460 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43461 // it was the old xcomponent building that caused this before.
43462 // espeically if border is the top element in the tree.
43472 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43474 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43475 this.add(region, ret);
43479 throw "Can not add '" + cfg.xtype + "' to Border";
43485 this.beginUpdate();
43489 Roo.each(xitems, function(i) {
43490 region = nb && i.region ? i.region : false;
43492 var add = ret.addxtype(i);
43495 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43496 if (!i.background) {
43497 abn[region] = nb[region] ;
43504 // make the last non-background panel active..
43505 //if (nb) { Roo.log(abn); }
43508 for(var r in abn) {
43509 region = this.getRegion(r);
43511 // tried using nb[r], but it does not work..
43513 region.showPanel(abn[r]);
43524 factory : function(cfg)
43527 var validRegions = Roo.bootstrap.layout.Border.regions;
43529 var target = cfg.region;
43532 var r = Roo.bootstrap.layout;
43536 return new r.North(cfg);
43538 return new r.South(cfg);
43540 return new r.East(cfg);
43542 return new r.West(cfg);
43544 return new r.Center(cfg);
43546 throw 'Layout region "'+target+'" not supported.';
43553 * Ext JS Library 1.1.1
43554 * Copyright(c) 2006-2007, Ext JS, LLC.
43556 * Originally Released Under LGPL - original licence link has changed is not relivant.
43559 * <script type="text/javascript">
43563 * @class Roo.bootstrap.layout.Basic
43564 * @extends Roo.util.Observable
43565 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43566 * and does not have a titlebar, tabs or any other features. All it does is size and position
43567 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43568 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43569 * @cfg {string} region the region that it inhabits..
43570 * @cfg {bool} skipConfig skip config?
43574 Roo.bootstrap.layout.Basic = function(config){
43576 this.mgr = config.mgr;
43578 this.position = config.region;
43580 var skipConfig = config.skipConfig;
43584 * @scope Roo.BasicLayoutRegion
43588 * @event beforeremove
43589 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43590 * @param {Roo.LayoutRegion} this
43591 * @param {Roo.ContentPanel} panel The panel
43592 * @param {Object} e The cancel event object
43594 "beforeremove" : true,
43596 * @event invalidated
43597 * Fires when the layout for this region is changed.
43598 * @param {Roo.LayoutRegion} this
43600 "invalidated" : true,
43602 * @event visibilitychange
43603 * Fires when this region is shown or hidden
43604 * @param {Roo.LayoutRegion} this
43605 * @param {Boolean} visibility true or false
43607 "visibilitychange" : true,
43609 * @event paneladded
43610 * Fires when a panel is added.
43611 * @param {Roo.LayoutRegion} this
43612 * @param {Roo.ContentPanel} panel The panel
43614 "paneladded" : true,
43616 * @event panelremoved
43617 * Fires when a panel is removed.
43618 * @param {Roo.LayoutRegion} this
43619 * @param {Roo.ContentPanel} panel The panel
43621 "panelremoved" : true,
43623 * @event beforecollapse
43624 * Fires when this region before collapse.
43625 * @param {Roo.LayoutRegion} this
43627 "beforecollapse" : true,
43630 * Fires when this region is collapsed.
43631 * @param {Roo.LayoutRegion} this
43633 "collapsed" : true,
43636 * Fires when this region is expanded.
43637 * @param {Roo.LayoutRegion} this
43642 * Fires when this region is slid into view.
43643 * @param {Roo.LayoutRegion} this
43645 "slideshow" : true,
43648 * Fires when this region slides out of view.
43649 * @param {Roo.LayoutRegion} this
43651 "slidehide" : true,
43653 * @event panelactivated
43654 * Fires when a panel is activated.
43655 * @param {Roo.LayoutRegion} this
43656 * @param {Roo.ContentPanel} panel The activated panel
43658 "panelactivated" : true,
43661 * Fires when the user resizes this region.
43662 * @param {Roo.LayoutRegion} this
43663 * @param {Number} newSize The new size (width for east/west, height for north/south)
43667 /** A collection of panels in this region. @type Roo.util.MixedCollection */
43668 this.panels = new Roo.util.MixedCollection();
43669 this.panels.getKey = this.getPanelId.createDelegate(this);
43671 this.activePanel = null;
43672 // ensure listeners are added...
43674 if (config.listeners || config.events) {
43675 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43676 listeners : config.listeners || {},
43677 events : config.events || {}
43681 if(skipConfig !== true){
43682 this.applyConfig(config);
43686 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43688 getPanelId : function(p){
43692 applyConfig : function(config){
43693 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43694 this.config = config;
43699 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
43700 * the width, for horizontal (north, south) the height.
43701 * @param {Number} newSize The new width or height
43703 resizeTo : function(newSize){
43704 var el = this.el ? this.el :
43705 (this.activePanel ? this.activePanel.getEl() : null);
43707 switch(this.position){
43710 el.setWidth(newSize);
43711 this.fireEvent("resized", this, newSize);
43715 el.setHeight(newSize);
43716 this.fireEvent("resized", this, newSize);
43722 getBox : function(){
43723 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43726 getMargins : function(){
43727 return this.margins;
43730 updateBox : function(box){
43732 var el = this.activePanel.getEl();
43733 el.dom.style.left = box.x + "px";
43734 el.dom.style.top = box.y + "px";
43735 this.activePanel.setSize(box.width, box.height);
43739 * Returns the container element for this region.
43740 * @return {Roo.Element}
43742 getEl : function(){
43743 return this.activePanel;
43747 * Returns true if this region is currently visible.
43748 * @return {Boolean}
43750 isVisible : function(){
43751 return this.activePanel ? true : false;
43754 setActivePanel : function(panel){
43755 panel = this.getPanel(panel);
43756 if(this.activePanel && this.activePanel != panel){
43757 this.activePanel.setActiveState(false);
43758 this.activePanel.getEl().setLeftTop(-10000,-10000);
43760 this.activePanel = panel;
43761 panel.setActiveState(true);
43763 panel.setSize(this.box.width, this.box.height);
43765 this.fireEvent("panelactivated", this, panel);
43766 this.fireEvent("invalidated");
43770 * Show the specified panel.
43771 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43772 * @return {Roo.ContentPanel} The shown panel or null
43774 showPanel : function(panel){
43775 panel = this.getPanel(panel);
43777 this.setActivePanel(panel);
43783 * Get the active panel for this region.
43784 * @return {Roo.ContentPanel} The active panel or null
43786 getActivePanel : function(){
43787 return this.activePanel;
43791 * Add the passed ContentPanel(s)
43792 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43793 * @return {Roo.ContentPanel} The panel added (if only one was added)
43795 add : function(panel){
43796 if(arguments.length > 1){
43797 for(var i = 0, len = arguments.length; i < len; i++) {
43798 this.add(arguments[i]);
43802 if(this.hasPanel(panel)){
43803 this.showPanel(panel);
43806 var el = panel.getEl();
43807 if(el.dom.parentNode != this.mgr.el.dom){
43808 this.mgr.el.dom.appendChild(el.dom);
43810 if(panel.setRegion){
43811 panel.setRegion(this);
43813 this.panels.add(panel);
43814 el.setStyle("position", "absolute");
43815 if(!panel.background){
43816 this.setActivePanel(panel);
43817 if(this.config.initialSize && this.panels.getCount()==1){
43818 this.resizeTo(this.config.initialSize);
43821 this.fireEvent("paneladded", this, panel);
43826 * Returns true if the panel is in this region.
43827 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43828 * @return {Boolean}
43830 hasPanel : function(panel){
43831 if(typeof panel == "object"){ // must be panel obj
43832 panel = panel.getId();
43834 return this.getPanel(panel) ? true : false;
43838 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43839 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43840 * @param {Boolean} preservePanel Overrides the config preservePanel option
43841 * @return {Roo.ContentPanel} The panel that was removed
43843 remove : function(panel, preservePanel){
43844 panel = this.getPanel(panel);
43849 this.fireEvent("beforeremove", this, panel, e);
43850 if(e.cancel === true){
43853 var panelId = panel.getId();
43854 this.panels.removeKey(panelId);
43859 * Returns the panel specified or null if it's not in this region.
43860 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43861 * @return {Roo.ContentPanel}
43863 getPanel : function(id){
43864 if(typeof id == "object"){ // must be panel obj
43867 return this.panels.get(id);
43871 * Returns this regions position (north/south/east/west/center).
43874 getPosition: function(){
43875 return this.position;
43879 * Ext JS Library 1.1.1
43880 * Copyright(c) 2006-2007, Ext JS, LLC.
43882 * Originally Released Under LGPL - original licence link has changed is not relivant.
43885 * <script type="text/javascript">
43889 * @class Roo.bootstrap.layout.Region
43890 * @extends Roo.bootstrap.layout.Basic
43891 * This class represents a region in a layout manager.
43893 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43894 * @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})
43895 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43896 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43897 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43898 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43899 * @cfg {String} title The title for the region (overrides panel titles)
43900 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43901 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43902 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43903 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43904 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43905 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43906 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43907 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43908 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43909 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43911 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43912 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43913 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43914 * @cfg {Number} width For East/West panels
43915 * @cfg {Number} height For North/South panels
43916 * @cfg {Boolean} split To show the splitter
43917 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43919 * @cfg {string} cls Extra CSS classes to add to region
43921 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43922 * @cfg {string} region the region that it inhabits..
43925 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43926 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43928 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43929 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43930 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43932 Roo.bootstrap.layout.Region = function(config)
43934 this.applyConfig(config);
43936 var mgr = config.mgr;
43937 var pos = config.region;
43938 config.skipConfig = true;
43939 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43942 this.onRender(mgr.el);
43945 this.visible = true;
43946 this.collapsed = false;
43947 this.unrendered_panels = [];
43950 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43952 position: '', // set by wrapper (eg. north/south etc..)
43953 unrendered_panels : null, // unrendered panels.
43955 tabPosition : false,
43957 mgr: false, // points to 'Border'
43960 createBody : function(){
43961 /** This region's body element
43962 * @type Roo.Element */
43963 this.bodyEl = this.el.createChild({
43965 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43969 onRender: function(ctr, pos)
43971 var dh = Roo.DomHelper;
43972 /** This region's container element
43973 * @type Roo.Element */
43974 this.el = dh.append(ctr.dom, {
43976 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43978 /** This region's title element
43979 * @type Roo.Element */
43981 this.titleEl = dh.append(this.el.dom, {
43983 unselectable: "on",
43984 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43986 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43987 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43991 this.titleEl.enableDisplayMode();
43992 /** This region's title text element
43993 * @type HTMLElement */
43994 this.titleTextEl = this.titleEl.dom.firstChild;
43995 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43997 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43998 this.closeBtn.enableDisplayMode();
43999 this.closeBtn.on("click", this.closeClicked, this);
44000 this.closeBtn.hide();
44002 this.createBody(this.config);
44003 if(this.config.hideWhenEmpty){
44005 this.on("paneladded", this.validateVisibility, this);
44006 this.on("panelremoved", this.validateVisibility, this);
44008 if(this.autoScroll){
44009 this.bodyEl.setStyle("overflow", "auto");
44011 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44013 //if(c.titlebar !== false){
44014 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44015 this.titleEl.hide();
44017 this.titleEl.show();
44018 if(this.config.title){
44019 this.titleTextEl.innerHTML = this.config.title;
44023 if(this.config.collapsed){
44024 this.collapse(true);
44026 if(this.config.hidden){
44030 if (this.unrendered_panels && this.unrendered_panels.length) {
44031 for (var i =0;i< this.unrendered_panels.length; i++) {
44032 this.add(this.unrendered_panels[i]);
44034 this.unrendered_panels = null;
44040 applyConfig : function(c)
44043 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44044 var dh = Roo.DomHelper;
44045 if(c.titlebar !== false){
44046 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44047 this.collapseBtn.on("click", this.collapse, this);
44048 this.collapseBtn.enableDisplayMode();
44050 if(c.showPin === true || this.showPin){
44051 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44052 this.stickBtn.enableDisplayMode();
44053 this.stickBtn.on("click", this.expand, this);
44054 this.stickBtn.hide();
44059 /** This region's collapsed element
44060 * @type Roo.Element */
44063 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44064 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44067 if(c.floatable !== false){
44068 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44069 this.collapsedEl.on("click", this.collapseClick, this);
44072 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44073 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44074 id: "message", unselectable: "on", style:{"float":"left"}});
44075 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44077 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44078 this.expandBtn.on("click", this.expand, this);
44082 if(this.collapseBtn){
44083 this.collapseBtn.setVisible(c.collapsible == true);
44086 this.cmargins = c.cmargins || this.cmargins ||
44087 (this.position == "west" || this.position == "east" ?
44088 {top: 0, left: 2, right:2, bottom: 0} :
44089 {top: 2, left: 0, right:0, bottom: 2});
44091 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44094 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44096 this.autoScroll = c.autoScroll || false;
44101 this.duration = c.duration || .30;
44102 this.slideDuration = c.slideDuration || .45;
44107 * Returns true if this region is currently visible.
44108 * @return {Boolean}
44110 isVisible : function(){
44111 return this.visible;
44115 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44116 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
44118 //setCollapsedTitle : function(title){
44119 // title = title || " ";
44120 // if(this.collapsedTitleTextEl){
44121 // this.collapsedTitleTextEl.innerHTML = title;
44125 getBox : function(){
44127 // if(!this.collapsed){
44128 b = this.el.getBox(false, true);
44130 // b = this.collapsedEl.getBox(false, true);
44135 getMargins : function(){
44136 return this.margins;
44137 //return this.collapsed ? this.cmargins : this.margins;
44140 highlight : function(){
44141 this.el.addClass("x-layout-panel-dragover");
44144 unhighlight : function(){
44145 this.el.removeClass("x-layout-panel-dragover");
44148 updateBox : function(box)
44150 if (!this.bodyEl) {
44151 return; // not rendered yet..
44155 if(!this.collapsed){
44156 this.el.dom.style.left = box.x + "px";
44157 this.el.dom.style.top = box.y + "px";
44158 this.updateBody(box.width, box.height);
44160 this.collapsedEl.dom.style.left = box.x + "px";
44161 this.collapsedEl.dom.style.top = box.y + "px";
44162 this.collapsedEl.setSize(box.width, box.height);
44165 this.tabs.autoSizeTabs();
44169 updateBody : function(w, h)
44172 this.el.setWidth(w);
44173 w -= this.el.getBorderWidth("rl");
44174 if(this.config.adjustments){
44175 w += this.config.adjustments[0];
44178 if(h !== null && h > 0){
44179 this.el.setHeight(h);
44180 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44181 h -= this.el.getBorderWidth("tb");
44182 if(this.config.adjustments){
44183 h += this.config.adjustments[1];
44185 this.bodyEl.setHeight(h);
44187 h = this.tabs.syncHeight(h);
44190 if(this.panelSize){
44191 w = w !== null ? w : this.panelSize.width;
44192 h = h !== null ? h : this.panelSize.height;
44194 if(this.activePanel){
44195 var el = this.activePanel.getEl();
44196 w = w !== null ? w : el.getWidth();
44197 h = h !== null ? h : el.getHeight();
44198 this.panelSize = {width: w, height: h};
44199 this.activePanel.setSize(w, h);
44201 if(Roo.isIE && this.tabs){
44202 this.tabs.el.repaint();
44207 * Returns the container element for this region.
44208 * @return {Roo.Element}
44210 getEl : function(){
44215 * Hides this region.
44218 //if(!this.collapsed){
44219 this.el.dom.style.left = "-2000px";
44222 // this.collapsedEl.dom.style.left = "-2000px";
44223 // this.collapsedEl.hide();
44225 this.visible = false;
44226 this.fireEvent("visibilitychange", this, false);
44230 * Shows this region if it was previously hidden.
44233 //if(!this.collapsed){
44236 // this.collapsedEl.show();
44238 this.visible = true;
44239 this.fireEvent("visibilitychange", this, true);
44242 closeClicked : function(){
44243 if(this.activePanel){
44244 this.remove(this.activePanel);
44248 collapseClick : function(e){
44250 e.stopPropagation();
44253 e.stopPropagation();
44259 * Collapses this region.
44260 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44263 collapse : function(skipAnim, skipCheck = false){
44264 if(this.collapsed) {
44268 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44270 this.collapsed = true;
44272 this.split.el.hide();
44274 if(this.config.animate && skipAnim !== true){
44275 this.fireEvent("invalidated", this);
44276 this.animateCollapse();
44278 this.el.setLocation(-20000,-20000);
44280 this.collapsedEl.show();
44281 this.fireEvent("collapsed", this);
44282 this.fireEvent("invalidated", this);
44288 animateCollapse : function(){
44293 * Expands this region if it was previously collapsed.
44294 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44295 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44298 expand : function(e, skipAnim){
44300 e.stopPropagation();
44302 if(!this.collapsed || this.el.hasActiveFx()) {
44306 this.afterSlideIn();
44309 this.collapsed = false;
44310 if(this.config.animate && skipAnim !== true){
44311 this.animateExpand();
44315 this.split.el.show();
44317 this.collapsedEl.setLocation(-2000,-2000);
44318 this.collapsedEl.hide();
44319 this.fireEvent("invalidated", this);
44320 this.fireEvent("expanded", this);
44324 animateExpand : function(){
44328 initTabs : function()
44330 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44332 var ts = new Roo.bootstrap.panel.Tabs({
44333 el: this.bodyEl.dom,
44335 tabPosition: this.tabPosition ? this.tabPosition : 'top',
44336 disableTooltips: this.config.disableTabTips,
44337 toolbar : this.config.toolbar
44340 if(this.config.hideTabs){
44341 ts.stripWrap.setDisplayed(false);
44344 ts.resizeTabs = this.config.resizeTabs === true;
44345 ts.minTabWidth = this.config.minTabWidth || 40;
44346 ts.maxTabWidth = this.config.maxTabWidth || 250;
44347 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44348 ts.monitorResize = false;
44349 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44350 ts.bodyEl.addClass('roo-layout-tabs-body');
44351 this.panels.each(this.initPanelAsTab, this);
44354 initPanelAsTab : function(panel){
44355 var ti = this.tabs.addTab(
44359 this.config.closeOnTab && panel.isClosable(),
44362 if(panel.tabTip !== undefined){
44363 ti.setTooltip(panel.tabTip);
44365 ti.on("activate", function(){
44366 this.setActivePanel(panel);
44369 if(this.config.closeOnTab){
44370 ti.on("beforeclose", function(t, e){
44372 this.remove(panel);
44376 panel.tabItem = ti;
44381 updatePanelTitle : function(panel, title)
44383 if(this.activePanel == panel){
44384 this.updateTitle(title);
44387 var ti = this.tabs.getTab(panel.getEl().id);
44389 if(panel.tabTip !== undefined){
44390 ti.setTooltip(panel.tabTip);
44395 updateTitle : function(title){
44396 if(this.titleTextEl && !this.config.title){
44397 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
44401 setActivePanel : function(panel)
44403 panel = this.getPanel(panel);
44404 if(this.activePanel && this.activePanel != panel){
44405 if(this.activePanel.setActiveState(false) === false){
44409 this.activePanel = panel;
44410 panel.setActiveState(true);
44411 if(this.panelSize){
44412 panel.setSize(this.panelSize.width, this.panelSize.height);
44415 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44417 this.updateTitle(panel.getTitle());
44419 this.fireEvent("invalidated", this);
44421 this.fireEvent("panelactivated", this, panel);
44425 * Shows the specified panel.
44426 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44427 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44429 showPanel : function(panel)
44431 panel = this.getPanel(panel);
44434 var tab = this.tabs.getTab(panel.getEl().id);
44435 if(tab.isHidden()){
44436 this.tabs.unhideTab(tab.id);
44440 this.setActivePanel(panel);
44447 * Get the active panel for this region.
44448 * @return {Roo.ContentPanel} The active panel or null
44450 getActivePanel : function(){
44451 return this.activePanel;
44454 validateVisibility : function(){
44455 if(this.panels.getCount() < 1){
44456 this.updateTitle(" ");
44457 this.closeBtn.hide();
44460 if(!this.isVisible()){
44467 * Adds the passed ContentPanel(s) to this region.
44468 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44469 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44471 add : function(panel)
44473 if(arguments.length > 1){
44474 for(var i = 0, len = arguments.length; i < len; i++) {
44475 this.add(arguments[i]);
44480 // if we have not been rendered yet, then we can not really do much of this..
44481 if (!this.bodyEl) {
44482 this.unrendered_panels.push(panel);
44489 if(this.hasPanel(panel)){
44490 this.showPanel(panel);
44493 panel.setRegion(this);
44494 this.panels.add(panel);
44495 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44496 // sinle panel - no tab...?? would it not be better to render it with the tabs,
44497 // and hide them... ???
44498 this.bodyEl.dom.appendChild(panel.getEl().dom);
44499 if(panel.background !== true){
44500 this.setActivePanel(panel);
44502 this.fireEvent("paneladded", this, panel);
44509 this.initPanelAsTab(panel);
44513 if(panel.background !== true){
44514 this.tabs.activate(panel.getEl().id);
44516 this.fireEvent("paneladded", this, panel);
44521 * Hides the tab for the specified panel.
44522 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44524 hidePanel : function(panel){
44525 if(this.tabs && (panel = this.getPanel(panel))){
44526 this.tabs.hideTab(panel.getEl().id);
44531 * Unhides the tab for a previously hidden panel.
44532 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44534 unhidePanel : function(panel){
44535 if(this.tabs && (panel = this.getPanel(panel))){
44536 this.tabs.unhideTab(panel.getEl().id);
44540 clearPanels : function(){
44541 while(this.panels.getCount() > 0){
44542 this.remove(this.panels.first());
44547 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44548 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44549 * @param {Boolean} preservePanel Overrides the config preservePanel option
44550 * @return {Roo.ContentPanel} The panel that was removed
44552 remove : function(panel, preservePanel)
44554 panel = this.getPanel(panel);
44559 this.fireEvent("beforeremove", this, panel, e);
44560 if(e.cancel === true){
44563 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44564 var panelId = panel.getId();
44565 this.panels.removeKey(panelId);
44567 document.body.appendChild(panel.getEl().dom);
44570 this.tabs.removeTab(panel.getEl().id);
44571 }else if (!preservePanel){
44572 this.bodyEl.dom.removeChild(panel.getEl().dom);
44574 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44575 var p = this.panels.first();
44576 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44577 tempEl.appendChild(p.getEl().dom);
44578 this.bodyEl.update("");
44579 this.bodyEl.dom.appendChild(p.getEl().dom);
44581 this.updateTitle(p.getTitle());
44583 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44584 this.setActivePanel(p);
44586 panel.setRegion(null);
44587 if(this.activePanel == panel){
44588 this.activePanel = null;
44590 if(this.config.autoDestroy !== false && preservePanel !== true){
44591 try{panel.destroy();}catch(e){}
44593 this.fireEvent("panelremoved", this, panel);
44598 * Returns the TabPanel component used by this region
44599 * @return {Roo.TabPanel}
44601 getTabs : function(){
44605 createTool : function(parentEl, className){
44606 var btn = Roo.DomHelper.append(parentEl, {
44608 cls: "x-layout-tools-button",
44611 cls: "roo-layout-tools-button-inner " + className,
44615 btn.addClassOnOver("roo-layout-tools-button-over");
44620 * Ext JS Library 1.1.1
44621 * Copyright(c) 2006-2007, Ext JS, LLC.
44623 * Originally Released Under LGPL - original licence link has changed is not relivant.
44626 * <script type="text/javascript">
44632 * @class Roo.SplitLayoutRegion
44633 * @extends Roo.LayoutRegion
44634 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44636 Roo.bootstrap.layout.Split = function(config){
44637 this.cursor = config.cursor;
44638 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44641 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44643 splitTip : "Drag to resize.",
44644 collapsibleSplitTip : "Drag to resize. Double click to hide.",
44645 useSplitTips : false,
44647 applyConfig : function(config){
44648 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44651 onRender : function(ctr,pos) {
44653 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44654 if(!this.config.split){
44659 var splitEl = Roo.DomHelper.append(ctr.dom, {
44661 id: this.el.id + "-split",
44662 cls: "roo-layout-split roo-layout-split-"+this.position,
44665 /** The SplitBar for this region
44666 * @type Roo.SplitBar */
44667 // does not exist yet...
44668 Roo.log([this.position, this.orientation]);
44670 this.split = new Roo.bootstrap.SplitBar({
44671 dragElement : splitEl,
44672 resizingElement: this.el,
44673 orientation : this.orientation
44676 this.split.on("moved", this.onSplitMove, this);
44677 this.split.useShim = this.config.useShim === true;
44678 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44679 if(this.useSplitTips){
44680 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44682 //if(config.collapsible){
44683 // this.split.el.on("dblclick", this.collapse, this);
44686 if(typeof this.config.minSize != "undefined"){
44687 this.split.minSize = this.config.minSize;
44689 if(typeof this.config.maxSize != "undefined"){
44690 this.split.maxSize = this.config.maxSize;
44692 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44693 this.hideSplitter();
44698 getHMaxSize : function(){
44699 var cmax = this.config.maxSize || 10000;
44700 var center = this.mgr.getRegion("center");
44701 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44704 getVMaxSize : function(){
44705 var cmax = this.config.maxSize || 10000;
44706 var center = this.mgr.getRegion("center");
44707 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44710 onSplitMove : function(split, newSize){
44711 this.fireEvent("resized", this, newSize);
44715 * Returns the {@link Roo.SplitBar} for this region.
44716 * @return {Roo.SplitBar}
44718 getSplitBar : function(){
44723 this.hideSplitter();
44724 Roo.bootstrap.layout.Split.superclass.hide.call(this);
44727 hideSplitter : function(){
44729 this.split.el.setLocation(-2000,-2000);
44730 this.split.el.hide();
44736 this.split.el.show();
44738 Roo.bootstrap.layout.Split.superclass.show.call(this);
44741 beforeSlide: function(){
44742 if(Roo.isGecko){// firefox overflow auto bug workaround
44743 this.bodyEl.clip();
44745 this.tabs.bodyEl.clip();
44747 if(this.activePanel){
44748 this.activePanel.getEl().clip();
44750 if(this.activePanel.beforeSlide){
44751 this.activePanel.beforeSlide();
44757 afterSlide : function(){
44758 if(Roo.isGecko){// firefox overflow auto bug workaround
44759 this.bodyEl.unclip();
44761 this.tabs.bodyEl.unclip();
44763 if(this.activePanel){
44764 this.activePanel.getEl().unclip();
44765 if(this.activePanel.afterSlide){
44766 this.activePanel.afterSlide();
44772 initAutoHide : function(){
44773 if(this.autoHide !== false){
44774 if(!this.autoHideHd){
44775 var st = new Roo.util.DelayedTask(this.slideIn, this);
44776 this.autoHideHd = {
44777 "mouseout": function(e){
44778 if(!e.within(this.el, true)){
44782 "mouseover" : function(e){
44788 this.el.on(this.autoHideHd);
44792 clearAutoHide : function(){
44793 if(this.autoHide !== false){
44794 this.el.un("mouseout", this.autoHideHd.mouseout);
44795 this.el.un("mouseover", this.autoHideHd.mouseover);
44799 clearMonitor : function(){
44800 Roo.get(document).un("click", this.slideInIf, this);
44803 // these names are backwards but not changed for compat
44804 slideOut : function(){
44805 if(this.isSlid || this.el.hasActiveFx()){
44808 this.isSlid = true;
44809 if(this.collapseBtn){
44810 this.collapseBtn.hide();
44812 this.closeBtnState = this.closeBtn.getStyle('display');
44813 this.closeBtn.hide();
44815 this.stickBtn.show();
44818 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44819 this.beforeSlide();
44820 this.el.setStyle("z-index", 10001);
44821 this.el.slideIn(this.getSlideAnchor(), {
44822 callback: function(){
44824 this.initAutoHide();
44825 Roo.get(document).on("click", this.slideInIf, this);
44826 this.fireEvent("slideshow", this);
44833 afterSlideIn : function(){
44834 this.clearAutoHide();
44835 this.isSlid = false;
44836 this.clearMonitor();
44837 this.el.setStyle("z-index", "");
44838 if(this.collapseBtn){
44839 this.collapseBtn.show();
44841 this.closeBtn.setStyle('display', this.closeBtnState);
44843 this.stickBtn.hide();
44845 this.fireEvent("slidehide", this);
44848 slideIn : function(cb){
44849 if(!this.isSlid || this.el.hasActiveFx()){
44853 this.isSlid = false;
44854 this.beforeSlide();
44855 this.el.slideOut(this.getSlideAnchor(), {
44856 callback: function(){
44857 this.el.setLeftTop(-10000, -10000);
44859 this.afterSlideIn();
44867 slideInIf : function(e){
44868 if(!e.within(this.el)){
44873 animateCollapse : function(){
44874 this.beforeSlide();
44875 this.el.setStyle("z-index", 20000);
44876 var anchor = this.getSlideAnchor();
44877 this.el.slideOut(anchor, {
44878 callback : function(){
44879 this.el.setStyle("z-index", "");
44880 this.collapsedEl.slideIn(anchor, {duration:.3});
44882 this.el.setLocation(-10000,-10000);
44884 this.fireEvent("collapsed", this);
44891 animateExpand : function(){
44892 this.beforeSlide();
44893 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44894 this.el.setStyle("z-index", 20000);
44895 this.collapsedEl.hide({
44898 this.el.slideIn(this.getSlideAnchor(), {
44899 callback : function(){
44900 this.el.setStyle("z-index", "");
44903 this.split.el.show();
44905 this.fireEvent("invalidated", this);
44906 this.fireEvent("expanded", this);
44934 getAnchor : function(){
44935 return this.anchors[this.position];
44938 getCollapseAnchor : function(){
44939 return this.canchors[this.position];
44942 getSlideAnchor : function(){
44943 return this.sanchors[this.position];
44946 getAlignAdj : function(){
44947 var cm = this.cmargins;
44948 switch(this.position){
44964 getExpandAdj : function(){
44965 var c = this.collapsedEl, cm = this.cmargins;
44966 switch(this.position){
44968 return [-(cm.right+c.getWidth()+cm.left), 0];
44971 return [cm.right+c.getWidth()+cm.left, 0];
44974 return [0, -(cm.top+cm.bottom+c.getHeight())];
44977 return [0, cm.top+cm.bottom+c.getHeight()];
44983 * Ext JS Library 1.1.1
44984 * Copyright(c) 2006-2007, Ext JS, LLC.
44986 * Originally Released Under LGPL - original licence link has changed is not relivant.
44989 * <script type="text/javascript">
44992 * These classes are private internal classes
44994 Roo.bootstrap.layout.Center = function(config){
44995 config.region = "center";
44996 Roo.bootstrap.layout.Region.call(this, config);
44997 this.visible = true;
44998 this.minWidth = config.minWidth || 20;
44999 this.minHeight = config.minHeight || 20;
45002 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
45004 // center panel can't be hidden
45008 // center panel can't be hidden
45011 getMinWidth: function(){
45012 return this.minWidth;
45015 getMinHeight: function(){
45016 return this.minHeight;
45030 Roo.bootstrap.layout.North = function(config)
45032 config.region = 'north';
45033 config.cursor = 'n-resize';
45035 Roo.bootstrap.layout.Split.call(this, config);
45039 this.split.placement = Roo.bootstrap.SplitBar.TOP;
45040 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45041 this.split.el.addClass("roo-layout-split-v");
45043 //var size = config.initialSize || config.height;
45044 //if(this.el && typeof size != "undefined"){
45045 // this.el.setHeight(size);
45048 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45050 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45053 onRender : function(ctr, pos)
45055 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45056 var size = this.config.initialSize || this.config.height;
45057 if(this.el && typeof size != "undefined"){
45058 this.el.setHeight(size);
45063 getBox : function(){
45064 if(this.collapsed){
45065 return this.collapsedEl.getBox();
45067 var box = this.el.getBox();
45069 box.height += this.split.el.getHeight();
45074 updateBox : function(box){
45075 if(this.split && !this.collapsed){
45076 box.height -= this.split.el.getHeight();
45077 this.split.el.setLeft(box.x);
45078 this.split.el.setTop(box.y+box.height);
45079 this.split.el.setWidth(box.width);
45081 if(this.collapsed){
45082 this.updateBody(box.width, null);
45084 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45092 Roo.bootstrap.layout.South = function(config){
45093 config.region = 'south';
45094 config.cursor = 's-resize';
45095 Roo.bootstrap.layout.Split.call(this, config);
45097 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45098 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45099 this.split.el.addClass("roo-layout-split-v");
45104 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45105 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45107 onRender : function(ctr, pos)
45109 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45110 var size = this.config.initialSize || this.config.height;
45111 if(this.el && typeof size != "undefined"){
45112 this.el.setHeight(size);
45117 getBox : function(){
45118 if(this.collapsed){
45119 return this.collapsedEl.getBox();
45121 var box = this.el.getBox();
45123 var sh = this.split.el.getHeight();
45130 updateBox : function(box){
45131 if(this.split && !this.collapsed){
45132 var sh = this.split.el.getHeight();
45135 this.split.el.setLeft(box.x);
45136 this.split.el.setTop(box.y-sh);
45137 this.split.el.setWidth(box.width);
45139 if(this.collapsed){
45140 this.updateBody(box.width, null);
45142 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45146 Roo.bootstrap.layout.East = function(config){
45147 config.region = "east";
45148 config.cursor = "e-resize";
45149 Roo.bootstrap.layout.Split.call(this, config);
45151 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45152 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45153 this.split.el.addClass("roo-layout-split-h");
45157 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45158 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45160 onRender : function(ctr, pos)
45162 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45163 var size = this.config.initialSize || this.config.width;
45164 if(this.el && typeof size != "undefined"){
45165 this.el.setWidth(size);
45170 getBox : function(){
45171 if(this.collapsed){
45172 return this.collapsedEl.getBox();
45174 var box = this.el.getBox();
45176 var sw = this.split.el.getWidth();
45183 updateBox : function(box){
45184 if(this.split && !this.collapsed){
45185 var sw = this.split.el.getWidth();
45187 this.split.el.setLeft(box.x);
45188 this.split.el.setTop(box.y);
45189 this.split.el.setHeight(box.height);
45192 if(this.collapsed){
45193 this.updateBody(null, box.height);
45195 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45199 Roo.bootstrap.layout.West = function(config){
45200 config.region = "west";
45201 config.cursor = "w-resize";
45203 Roo.bootstrap.layout.Split.call(this, config);
45205 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45206 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45207 this.split.el.addClass("roo-layout-split-h");
45211 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45212 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45214 onRender: function(ctr, pos)
45216 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45217 var size = this.config.initialSize || this.config.width;
45218 if(typeof size != "undefined"){
45219 this.el.setWidth(size);
45223 getBox : function(){
45224 if(this.collapsed){
45225 return this.collapsedEl.getBox();
45227 var box = this.el.getBox();
45228 if (box.width == 0) {
45229 box.width = this.config.width; // kludge?
45232 box.width += this.split.el.getWidth();
45237 updateBox : function(box){
45238 if(this.split && !this.collapsed){
45239 var sw = this.split.el.getWidth();
45241 this.split.el.setLeft(box.x+box.width);
45242 this.split.el.setTop(box.y);
45243 this.split.el.setHeight(box.height);
45245 if(this.collapsed){
45246 this.updateBody(null, box.height);
45248 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45252 * Ext JS Library 1.1.1
45253 * Copyright(c) 2006-2007, Ext JS, LLC.
45255 * Originally Released Under LGPL - original licence link has changed is not relivant.
45258 * <script type="text/javascript">
45261 * @class Roo.bootstrap.paenl.Content
45262 * @extends Roo.util.Observable
45263 * @children Roo.bootstrap.Component
45264 * @parent builder Roo.bootstrap.layout.Border
45265 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45266 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
45267 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
45268 * @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
45269 * @cfg {Boolean} closable True if the panel can be closed/removed
45270 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45271 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45272 * @cfg {Toolbar} toolbar A toolbar for this panel
45273 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45274 * @cfg {String} title The title for this panel
45275 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45276 * @cfg {String} url Calls {@link #setUrl} with this value
45277 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45278 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45279 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45280 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
45281 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
45282 * @cfg {Boolean} badges render the badges
45283 * @cfg {String} cls extra classes to use
45284 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45287 * Create a new ContentPanel.
45288 * @param {String/Object} config A string to set only the title or a config object
45291 Roo.bootstrap.panel.Content = function( config){
45293 this.tpl = config.tpl || false;
45295 var el = config.el;
45296 var content = config.content;
45298 if(config.autoCreate){ // xtype is available if this is called from factory
45301 this.el = Roo.get(el);
45302 if(!this.el && config && config.autoCreate){
45303 if(typeof config.autoCreate == "object"){
45304 if(!config.autoCreate.id){
45305 config.autoCreate.id = config.id||el;
45307 this.el = Roo.DomHelper.append(document.body,
45308 config.autoCreate, true);
45312 cls: (config.cls || '') +
45313 (config.background ? ' bg-' + config.background : '') +
45314 " roo-layout-inactive-content",
45317 if (config.iframe) {
45321 style : 'border: 0px',
45322 src : 'about:blank'
45328 elcfg.html = config.html;
45332 this.el = Roo.DomHelper.append(document.body, elcfg , true);
45333 if (config.iframe) {
45334 this.iframeEl = this.el.select('iframe',true).first();
45339 this.closable = false;
45340 this.loaded = false;
45341 this.active = false;
45344 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45346 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45348 this.wrapEl = this.el; //this.el.wrap();
45350 if (config.toolbar.items) {
45351 ti = config.toolbar.items ;
45352 delete config.toolbar.items ;
45356 this.toolbar.render(this.wrapEl, 'before');
45357 for(var i =0;i < ti.length;i++) {
45358 // Roo.log(['add child', items[i]]);
45359 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45361 this.toolbar.items = nitems;
45362 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45363 delete config.toolbar;
45367 // xtype created footer. - not sure if will work as we normally have to render first..
45368 if (this.footer && !this.footer.el && this.footer.xtype) {
45369 if (!this.wrapEl) {
45370 this.wrapEl = this.el.wrap();
45373 this.footer.container = this.wrapEl.createChild();
45375 this.footer = Roo.factory(this.footer, Roo);
45380 if(typeof config == "string"){
45381 this.title = config;
45383 Roo.apply(this, config);
45387 this.resizeEl = Roo.get(this.resizeEl, true);
45389 this.resizeEl = this.el;
45391 // handle view.xtype
45399 * Fires when this panel is activated.
45400 * @param {Roo.ContentPanel} this
45404 * @event deactivate
45405 * Fires when this panel is activated.
45406 * @param {Roo.ContentPanel} this
45408 "deactivate" : true,
45412 * Fires when this panel is resized if fitToFrame is true.
45413 * @param {Roo.ContentPanel} this
45414 * @param {Number} width The width after any component adjustments
45415 * @param {Number} height The height after any component adjustments
45421 * Fires when this tab is created
45422 * @param {Roo.ContentPanel} this
45428 * Fires when this content is scrolled
45429 * @param {Roo.ContentPanel} this
45430 * @param {Event} scrollEvent
45441 if(this.autoScroll && !this.iframe){
45442 this.resizeEl.setStyle("overflow", "auto");
45443 this.resizeEl.on('scroll', this.onScroll, this);
45445 // fix randome scrolling
45446 //this.el.on('scroll', function() {
45447 // Roo.log('fix random scolling');
45448 // this.scrollTo('top',0);
45451 content = content || this.content;
45453 this.setContent(content);
45455 if(config && config.url){
45456 this.setUrl(this.url, this.params, this.loadOnce);
45461 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45463 if (this.view && typeof(this.view.xtype) != 'undefined') {
45464 this.view.el = this.el.appendChild(document.createElement("div"));
45465 this.view = Roo.factory(this.view);
45466 this.view.render && this.view.render(false, '');
45470 this.fireEvent('render', this);
45473 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45483 /* Resize Element - use this to work out scroll etc. */
45486 setRegion : function(region){
45487 this.region = region;
45488 this.setActiveClass(region && !this.background);
45492 setActiveClass: function(state)
45495 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45496 this.el.setStyle('position','relative');
45498 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45499 this.el.setStyle('position', 'absolute');
45504 * Returns the toolbar for this Panel if one was configured.
45505 * @return {Roo.Toolbar}
45507 getToolbar : function(){
45508 return this.toolbar;
45511 setActiveState : function(active)
45513 this.active = active;
45514 this.setActiveClass(active);
45516 if(this.fireEvent("deactivate", this) === false){
45521 this.fireEvent("activate", this);
45525 * Updates this panel's element (not for iframe)
45526 * @param {String} content The new content
45527 * @param {Boolean} loadScripts (optional) true to look for and process scripts
45529 setContent : function(content, loadScripts){
45534 this.el.update(content, loadScripts);
45537 ignoreResize : function(w, h)
45539 //return false; // always resize?
45540 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45543 this.lastSize = {width: w, height: h};
45548 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45549 * @return {Roo.UpdateManager} The UpdateManager
45551 getUpdateManager : function(){
45555 return this.el.getUpdateManager();
45558 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45559 * Does not work with IFRAME contents
45560 * @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:
45563 url: "your-url.php",
45564 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45565 callback: yourFunction,
45566 scope: yourObject, //(optional scope)
45569 text: "Loading...",
45575 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45576 * 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.
45577 * @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}
45578 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45579 * @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.
45580 * @return {Roo.ContentPanel} this
45588 var um = this.el.getUpdateManager();
45589 um.update.apply(um, arguments);
45595 * 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.
45596 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45597 * @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)
45598 * @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)
45599 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45601 setUrl : function(url, params, loadOnce){
45603 this.iframeEl.dom.src = url;
45607 if(this.refreshDelegate){
45608 this.removeListener("activate", this.refreshDelegate);
45610 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45611 this.on("activate", this.refreshDelegate);
45612 return this.el.getUpdateManager();
45615 _handleRefresh : function(url, params, loadOnce){
45616 if(!loadOnce || !this.loaded){
45617 var updater = this.el.getUpdateManager();
45618 updater.update(url, params, this._setLoaded.createDelegate(this));
45622 _setLoaded : function(){
45623 this.loaded = true;
45627 * Returns this panel's id
45630 getId : function(){
45635 * Returns this panel's element - used by regiosn to add.
45636 * @return {Roo.Element}
45638 getEl : function(){
45639 return this.wrapEl || this.el;
45644 adjustForComponents : function(width, height)
45646 //Roo.log('adjustForComponents ');
45647 if(this.resizeEl != this.el){
45648 width -= this.el.getFrameWidth('lr');
45649 height -= this.el.getFrameWidth('tb');
45652 var te = this.toolbar.getEl();
45653 te.setWidth(width);
45654 height -= te.getHeight();
45657 var te = this.footer.getEl();
45658 te.setWidth(width);
45659 height -= te.getHeight();
45663 if(this.adjustments){
45664 width += this.adjustments[0];
45665 height += this.adjustments[1];
45667 return {"width": width, "height": height};
45670 setSize : function(width, height){
45671 if(this.fitToFrame && !this.ignoreResize(width, height)){
45672 if(this.fitContainer && this.resizeEl != this.el){
45673 this.el.setSize(width, height);
45675 var size = this.adjustForComponents(width, height);
45677 this.iframeEl.setSize(width,height);
45680 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45681 this.fireEvent('resize', this, size.width, size.height);
45688 * Returns this panel's title
45691 getTitle : function(){
45693 if (typeof(this.title) != 'object') {
45698 for (var k in this.title) {
45699 if (!this.title.hasOwnProperty(k)) {
45703 if (k.indexOf('-') >= 0) {
45704 var s = k.split('-');
45705 for (var i = 0; i<s.length; i++) {
45706 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45709 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45716 * Set this panel's title
45717 * @param {String} title
45719 setTitle : function(title){
45720 this.title = title;
45722 this.region.updatePanelTitle(this, title);
45727 * Returns true is this panel was configured to be closable
45728 * @return {Boolean}
45730 isClosable : function(){
45731 return this.closable;
45734 beforeSlide : function(){
45736 this.resizeEl.clip();
45739 afterSlide : function(){
45741 this.resizeEl.unclip();
45745 * Force a content refresh from the URL specified in the {@link #setUrl} method.
45746 * Will fail silently if the {@link #setUrl} method has not been called.
45747 * This does not activate the panel, just updates its content.
45749 refresh : function(){
45750 if(this.refreshDelegate){
45751 this.loaded = false;
45752 this.refreshDelegate();
45757 * Destroys this panel
45759 destroy : function(){
45760 this.el.removeAllListeners();
45761 var tempEl = document.createElement("span");
45762 tempEl.appendChild(this.el.dom);
45763 tempEl.innerHTML = "";
45769 * form - if the content panel contains a form - this is a reference to it.
45770 * @type {Roo.form.Form}
45774 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45775 * This contains a reference to it.
45781 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45791 * @param {Object} cfg Xtype definition of item to add.
45795 getChildContainer: function () {
45796 return this.getEl();
45800 onScroll : function(e)
45802 this.fireEvent('scroll', this, e);
45807 var ret = new Roo.factory(cfg);
45812 if (cfg.xtype.match(/^Form$/)) {
45815 //if (this.footer) {
45816 // el = this.footer.container.insertSibling(false, 'before');
45818 el = this.el.createChild();
45821 this.form = new Roo.form.Form(cfg);
45824 if ( this.form.allItems.length) {
45825 this.form.render(el.dom);
45829 // should only have one of theses..
45830 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45831 // views.. should not be just added - used named prop 'view''
45833 cfg.el = this.el.appendChild(document.createElement("div"));
45836 var ret = new Roo.factory(cfg);
45838 ret.render && ret.render(false, ''); // render blank..
45848 * @class Roo.bootstrap.panel.Grid
45849 * @extends Roo.bootstrap.panel.Content
45851 * Create a new GridPanel.
45852 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45853 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45854 * @param {Object} config A the config object
45860 Roo.bootstrap.panel.Grid = function(config)
45864 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45865 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45867 config.el = this.wrapper;
45868 //this.el = this.wrapper;
45870 if (config.container) {
45871 // ctor'ed from a Border/panel.grid
45874 this.wrapper.setStyle("overflow", "hidden");
45875 this.wrapper.addClass('roo-grid-container');
45880 if(config.toolbar){
45881 var tool_el = this.wrapper.createChild();
45882 this.toolbar = Roo.factory(config.toolbar);
45884 if (config.toolbar.items) {
45885 ti = config.toolbar.items ;
45886 delete config.toolbar.items ;
45890 this.toolbar.render(tool_el);
45891 for(var i =0;i < ti.length;i++) {
45892 // Roo.log(['add child', items[i]]);
45893 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45895 this.toolbar.items = nitems;
45897 delete config.toolbar;
45900 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45901 config.grid.scrollBody = true;;
45902 config.grid.monitorWindowResize = false; // turn off autosizing
45903 config.grid.autoHeight = false;
45904 config.grid.autoWidth = false;
45906 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45908 if (config.background) {
45909 // render grid on panel activation (if panel background)
45910 this.on('activate', function(gp) {
45911 if (!gp.grid.rendered) {
45912 gp.grid.render(this.wrapper);
45913 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45918 this.grid.render(this.wrapper);
45919 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45922 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45923 // ??? needed ??? config.el = this.wrapper;
45928 // xtype created footer. - not sure if will work as we normally have to render first..
45929 if (this.footer && !this.footer.el && this.footer.xtype) {
45931 var ctr = this.grid.getView().getFooterPanel(true);
45932 this.footer.dataSource = this.grid.dataSource;
45933 this.footer = Roo.factory(this.footer, Roo);
45934 this.footer.render(ctr);
45944 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45947 getId : function(){
45948 return this.grid.id;
45952 * Returns the grid for this panel
45953 * @return {Roo.bootstrap.Table}
45955 getGrid : function(){
45959 setSize : function(width, height)
45962 //if(!this.ignoreResize(width, height)){
45963 var grid = this.grid;
45964 var size = this.adjustForComponents(width, height);
45965 // tfoot is not a footer?
45968 var gridel = grid.getGridEl();
45969 gridel.setSize(size.width, size.height);
45971 var tbd = grid.getGridEl().select('tbody', true).first();
45972 var thd = grid.getGridEl().select('thead',true).first();
45973 var tbf= grid.getGridEl().select('tfoot', true).first();
45976 size.height -= tbf.getHeight();
45979 size.height -= thd.getHeight();
45982 tbd.setSize(size.width, size.height );
45983 // this is for the account management tab -seems to work there.
45984 var thd = grid.getGridEl().select('thead',true).first();
45986 // tbd.setSize(size.width, size.height - thd.getHeight());
45996 beforeSlide : function(){
45997 this.grid.getView().scroller.clip();
46000 afterSlide : function(){
46001 this.grid.getView().scroller.unclip();
46004 destroy : function(){
46005 this.grid.destroy();
46007 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
46012 * @class Roo.bootstrap.panel.Nest
46013 * @extends Roo.bootstrap.panel.Content
46015 * Create a new Panel, that can contain a layout.Border.
46018 * @param {String/Object} config A string to set only the title or a config object
46020 Roo.bootstrap.panel.Nest = function(config)
46022 // construct with only one argument..
46023 /* FIXME - implement nicer consturctors
46024 if (layout.layout) {
46026 layout = config.layout;
46027 delete config.layout;
46029 if (layout.xtype && !layout.getEl) {
46030 // then layout needs constructing..
46031 layout = Roo.factory(layout, Roo);
46035 config.el = config.layout.getEl();
46037 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46039 config.layout.monitorWindowResize = false; // turn off autosizing
46040 this.layout = config.layout;
46041 this.layout.getEl().addClass("roo-layout-nested-layout");
46042 this.layout.parent = this;
46049 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46051 * @cfg {Roo.BorderLayout} layout The layout for this panel
46055 setSize : function(width, height){
46056 if(!this.ignoreResize(width, height)){
46057 var size = this.adjustForComponents(width, height);
46058 var el = this.layout.getEl();
46059 if (size.height < 1) {
46060 el.setWidth(size.width);
46062 el.setSize(size.width, size.height);
46064 var touch = el.dom.offsetWidth;
46065 this.layout.layout();
46066 // ie requires a double layout on the first pass
46067 if(Roo.isIE && !this.initialized){
46068 this.initialized = true;
46069 this.layout.layout();
46074 // activate all subpanels if not currently active..
46076 setActiveState : function(active){
46077 this.active = active;
46078 this.setActiveClass(active);
46081 this.fireEvent("deactivate", this);
46085 this.fireEvent("activate", this);
46086 // not sure if this should happen before or after..
46087 if (!this.layout) {
46088 return; // should not happen..
46091 for (var r in this.layout.regions) {
46092 reg = this.layout.getRegion(r);
46093 if (reg.getActivePanel()) {
46094 //reg.showPanel(reg.getActivePanel()); // force it to activate..
46095 reg.setActivePanel(reg.getActivePanel());
46098 if (!reg.panels.length) {
46101 reg.showPanel(reg.getPanel(0));
46110 * Returns the nested BorderLayout for this panel
46111 * @return {Roo.BorderLayout}
46113 getLayout : function(){
46114 return this.layout;
46118 * Adds a xtype elements to the layout of the nested panel
46122 xtype : 'ContentPanel',
46129 xtype : 'NestedLayoutPanel',
46135 items : [ ... list of content panels or nested layout panels.. ]
46139 * @param {Object} cfg Xtype definition of item to add.
46141 addxtype : function(cfg) {
46142 return this.layout.addxtype(cfg);
46147 * Ext JS Library 1.1.1
46148 * Copyright(c) 2006-2007, Ext JS, LLC.
46150 * Originally Released Under LGPL - original licence link has changed is not relivant.
46153 * <script type="text/javascript">
46156 * @class Roo.TabPanel
46157 * @extends Roo.util.Observable
46158 * A lightweight tab container.
46162 // basic tabs 1, built from existing content
46163 var tabs = new Roo.TabPanel("tabs1");
46164 tabs.addTab("script", "View Script");
46165 tabs.addTab("markup", "View Markup");
46166 tabs.activate("script");
46168 // more advanced tabs, built from javascript
46169 var jtabs = new Roo.TabPanel("jtabs");
46170 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46172 // set up the UpdateManager
46173 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46174 var updater = tab2.getUpdateManager();
46175 updater.setDefaultUrl("ajax1.htm");
46176 tab2.on('activate', updater.refresh, updater, true);
46178 // Use setUrl for Ajax loading
46179 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46180 tab3.setUrl("ajax2.htm", null, true);
46183 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46186 jtabs.activate("jtabs-1");
46189 * Create a new TabPanel.
46190 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46191 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46193 Roo.bootstrap.panel.Tabs = function(config){
46195 * The container element for this TabPanel.
46196 * @type Roo.Element
46198 this.el = Roo.get(config.el);
46201 if(typeof config == "boolean"){
46202 this.tabPosition = config ? "bottom" : "top";
46204 Roo.apply(this, config);
46208 if(this.tabPosition == "bottom"){
46209 // if tabs are at the bottom = create the body first.
46210 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46211 this.el.addClass("roo-tabs-bottom");
46213 // next create the tabs holders
46215 if (this.tabPosition == "west"){
46217 var reg = this.region; // fake it..
46219 if (!reg.mgr.parent) {
46222 reg = reg.mgr.parent.region;
46224 Roo.log("got nest?");
46226 if (reg.mgr.getRegion('west')) {
46227 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46228 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46229 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46230 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46231 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46239 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46240 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46241 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46242 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46247 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46250 // finally - if tabs are at the top, then create the body last..
46251 if(this.tabPosition != "bottom"){
46252 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46253 * @type Roo.Element
46255 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46256 this.el.addClass("roo-tabs-top");
46260 this.bodyEl.setStyle("position", "relative");
46262 this.active = null;
46263 this.activateDelegate = this.activate.createDelegate(this);
46268 * Fires when the active tab changes
46269 * @param {Roo.TabPanel} this
46270 * @param {Roo.TabPanelItem} activePanel The new active tab
46274 * @event beforetabchange
46275 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46276 * @param {Roo.TabPanel} this
46277 * @param {Object} e Set cancel to true on this object to cancel the tab change
46278 * @param {Roo.TabPanelItem} tab The tab being changed to
46280 "beforetabchange" : true
46283 Roo.EventManager.onWindowResize(this.onResize, this);
46284 this.cpad = this.el.getPadding("lr");
46285 this.hiddenCount = 0;
46288 // toolbar on the tabbar support...
46289 if (this.toolbar) {
46290 alert("no toolbar support yet");
46291 this.toolbar = false;
46293 var tcfg = this.toolbar;
46294 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
46295 this.toolbar = new Roo.Toolbar(tcfg);
46296 if (Roo.isSafari) {
46297 var tbl = tcfg.container.child('table', true);
46298 tbl.setAttribute('width', '100%');
46306 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46309 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46311 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46313 tabPosition : "top",
46315 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46317 currentTabWidth : 0,
46319 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46323 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46327 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46329 preferredTabWidth : 175,
46331 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46333 resizeTabs : false,
46335 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46337 monitorResize : true,
46339 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
46341 toolbar : false, // set by caller..
46343 region : false, /// set by caller
46345 disableTooltips : true, // not used yet...
46348 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46349 * @param {String} id The id of the div to use <b>or create</b>
46350 * @param {String} text The text for the tab
46351 * @param {String} content (optional) Content to put in the TabPanelItem body
46352 * @param {Boolean} closable (optional) True to create a close icon on the tab
46353 * @return {Roo.TabPanelItem} The created TabPanelItem
46355 addTab : function(id, text, content, closable, tpl)
46357 var item = new Roo.bootstrap.panel.TabItem({
46361 closable : closable,
46364 this.addTabItem(item);
46366 item.setContent(content);
46372 * Returns the {@link Roo.TabPanelItem} with the specified id/index
46373 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46374 * @return {Roo.TabPanelItem}
46376 getTab : function(id){
46377 return this.items[id];
46381 * Hides the {@link Roo.TabPanelItem} with the specified id/index
46382 * @param {String/Number} id The id or index of the TabPanelItem to hide.
46384 hideTab : function(id){
46385 var t = this.items[id];
46388 this.hiddenCount++;
46389 this.autoSizeTabs();
46394 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46395 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46397 unhideTab : function(id){
46398 var t = this.items[id];
46400 t.setHidden(false);
46401 this.hiddenCount--;
46402 this.autoSizeTabs();
46407 * Adds an existing {@link Roo.TabPanelItem}.
46408 * @param {Roo.TabPanelItem} item The TabPanelItem to add
46410 addTabItem : function(item)
46412 this.items[item.id] = item;
46413 this.items.push(item);
46414 this.autoSizeTabs();
46415 // if(this.resizeTabs){
46416 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46417 // this.autoSizeTabs();
46419 // item.autoSize();
46424 * Removes a {@link Roo.TabPanelItem}.
46425 * @param {String/Number} id The id or index of the TabPanelItem to remove.
46427 removeTab : function(id){
46428 var items = this.items;
46429 var tab = items[id];
46430 if(!tab) { return; }
46431 var index = items.indexOf(tab);
46432 if(this.active == tab && items.length > 1){
46433 var newTab = this.getNextAvailable(index);
46438 this.stripEl.dom.removeChild(tab.pnode.dom);
46439 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46440 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46442 items.splice(index, 1);
46443 delete this.items[tab.id];
46444 tab.fireEvent("close", tab);
46445 tab.purgeListeners();
46446 this.autoSizeTabs();
46449 getNextAvailable : function(start){
46450 var items = this.items;
46452 // look for a next tab that will slide over to
46453 // replace the one being removed
46454 while(index < items.length){
46455 var item = items[++index];
46456 if(item && !item.isHidden()){
46460 // if one isn't found select the previous tab (on the left)
46463 var item = items[--index];
46464 if(item && !item.isHidden()){
46472 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46473 * @param {String/Number} id The id or index of the TabPanelItem to disable.
46475 disableTab : function(id){
46476 var tab = this.items[id];
46477 if(tab && this.active != tab){
46483 * Enables a {@link Roo.TabPanelItem} that is disabled.
46484 * @param {String/Number} id The id or index of the TabPanelItem to enable.
46486 enableTab : function(id){
46487 var tab = this.items[id];
46492 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46493 * @param {String/Number} id The id or index of the TabPanelItem to activate.
46494 * @return {Roo.TabPanelItem} The TabPanelItem.
46496 activate : function(id)
46498 //Roo.log('activite:' + id);
46500 var tab = this.items[id];
46504 if(tab == this.active || tab.disabled){
46508 this.fireEvent("beforetabchange", this, e, tab);
46509 if(e.cancel !== true && !tab.disabled){
46511 this.active.hide();
46513 this.active = this.items[id];
46514 this.active.show();
46515 this.fireEvent("tabchange", this, this.active);
46521 * Gets the active {@link Roo.TabPanelItem}.
46522 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46524 getActiveTab : function(){
46525 return this.active;
46529 * Updates the tab body element to fit the height of the container element
46530 * for overflow scrolling
46531 * @param {Number} targetHeight (optional) Override the starting height from the elements height
46533 syncHeight : function(targetHeight){
46534 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46535 var bm = this.bodyEl.getMargins();
46536 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46537 this.bodyEl.setHeight(newHeight);
46541 onResize : function(){
46542 if(this.monitorResize){
46543 this.autoSizeTabs();
46548 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46550 beginUpdate : function(){
46551 this.updating = true;
46555 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46557 endUpdate : function(){
46558 this.updating = false;
46559 this.autoSizeTabs();
46563 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46565 autoSizeTabs : function()
46567 var count = this.items.length;
46568 var vcount = count - this.hiddenCount;
46571 this.stripEl.hide();
46573 this.stripEl.show();
46576 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46581 var w = Math.max(this.el.getWidth() - this.cpad, 10);
46582 var availWidth = Math.floor(w / vcount);
46583 var b = this.stripBody;
46584 if(b.getWidth() > w){
46585 var tabs = this.items;
46586 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46587 if(availWidth < this.minTabWidth){
46588 /*if(!this.sleft){ // incomplete scrolling code
46589 this.createScrollButtons();
46592 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46595 if(this.currentTabWidth < this.preferredTabWidth){
46596 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46602 * Returns the number of tabs in this TabPanel.
46605 getCount : function(){
46606 return this.items.length;
46610 * Resizes all the tabs to the passed width
46611 * @param {Number} The new width
46613 setTabWidth : function(width){
46614 this.currentTabWidth = width;
46615 for(var i = 0, len = this.items.length; i < len; i++) {
46616 if(!this.items[i].isHidden()) {
46617 this.items[i].setWidth(width);
46623 * Destroys this TabPanel
46624 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46626 destroy : function(removeEl){
46627 Roo.EventManager.removeResizeListener(this.onResize, this);
46628 for(var i = 0, len = this.items.length; i < len; i++){
46629 this.items[i].purgeListeners();
46631 if(removeEl === true){
46632 this.el.update("");
46637 createStrip : function(container)
46639 var strip = document.createElement("nav");
46640 strip.className = Roo.bootstrap.version == 4 ?
46641 "navbar-light bg-light" :
46642 "navbar navbar-default"; //"x-tabs-wrap";
46643 container.appendChild(strip);
46647 createStripList : function(strip)
46649 // div wrapper for retard IE
46650 // returns the "tr" element.
46651 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46652 //'<div class="x-tabs-strip-wrap">'+
46653 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46654 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46655 return strip.firstChild; //.firstChild.firstChild.firstChild;
46657 createBody : function(container)
46659 var body = document.createElement("div");
46660 Roo.id(body, "tab-body");
46661 //Roo.fly(body).addClass("x-tabs-body");
46662 Roo.fly(body).addClass("tab-content");
46663 container.appendChild(body);
46666 createItemBody :function(bodyEl, id){
46667 var body = Roo.getDom(id);
46669 body = document.createElement("div");
46672 //Roo.fly(body).addClass("x-tabs-item-body");
46673 Roo.fly(body).addClass("tab-pane");
46674 bodyEl.insertBefore(body, bodyEl.firstChild);
46678 createStripElements : function(stripEl, text, closable, tpl)
46680 var td = document.createElement("li"); // was td..
46681 td.className = 'nav-item';
46683 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46686 stripEl.appendChild(td);
46688 td.className = "x-tabs-closable";
46689 if(!this.closeTpl){
46690 this.closeTpl = new Roo.Template(
46691 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46692 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46693 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
46696 var el = this.closeTpl.overwrite(td, {"text": text});
46697 var close = el.getElementsByTagName("div")[0];
46698 var inner = el.getElementsByTagName("em")[0];
46699 return {"el": el, "close": close, "inner": inner};
46702 // not sure what this is..
46703 // if(!this.tabTpl){
46704 //this.tabTpl = new Roo.Template(
46705 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46706 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46708 // this.tabTpl = new Roo.Template(
46709 // '<a href="#">' +
46710 // '<span unselectable="on"' +
46711 // (this.disableTooltips ? '' : ' title="{text}"') +
46712 // ' >{text}</span></a>'
46718 var template = tpl || this.tabTpl || false;
46721 template = new Roo.Template(
46722 Roo.bootstrap.version == 4 ?
46724 '<a class="nav-link" href="#" unselectable="on"' +
46725 (this.disableTooltips ? '' : ' title="{text}"') +
46728 '<a class="nav-link" href="#">' +
46729 '<span unselectable="on"' +
46730 (this.disableTooltips ? '' : ' title="{text}"') +
46731 ' >{text}</span></a>'
46736 switch (typeof(template)) {
46740 template = new Roo.Template(template);
46746 var el = template.overwrite(td, {"text": text});
46748 var inner = el.getElementsByTagName("span")[0];
46750 return {"el": el, "inner": inner};
46758 * @class Roo.TabPanelItem
46759 * @extends Roo.util.Observable
46760 * Represents an individual item (tab plus body) in a TabPanel.
46761 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46762 * @param {String} id The id of this TabPanelItem
46763 * @param {String} text The text for the tab of this TabPanelItem
46764 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46766 Roo.bootstrap.panel.TabItem = function(config){
46768 * The {@link Roo.TabPanel} this TabPanelItem belongs to
46769 * @type Roo.TabPanel
46771 this.tabPanel = config.panel;
46773 * The id for this TabPanelItem
46776 this.id = config.id;
46778 this.disabled = false;
46780 this.text = config.text;
46782 this.loaded = false;
46783 this.closable = config.closable;
46786 * The body element for this TabPanelItem.
46787 * @type Roo.Element
46789 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46790 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46791 this.bodyEl.setStyle("display", "block");
46792 this.bodyEl.setStyle("zoom", "1");
46793 //this.hideAction();
46795 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46797 this.el = Roo.get(els.el);
46798 this.inner = Roo.get(els.inner, true);
46799 this.textEl = Roo.bootstrap.version == 4 ?
46800 this.el : Roo.get(this.el.dom.firstChild, true);
46802 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46803 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46806 // this.el.on("mousedown", this.onTabMouseDown, this);
46807 this.el.on("click", this.onTabClick, this);
46809 if(config.closable){
46810 var c = Roo.get(els.close, true);
46811 c.dom.title = this.closeText;
46812 c.addClassOnOver("close-over");
46813 c.on("click", this.closeClick, this);
46819 * Fires when this tab becomes the active tab.
46820 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46821 * @param {Roo.TabPanelItem} this
46825 * @event beforeclose
46826 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46827 * @param {Roo.TabPanelItem} this
46828 * @param {Object} e Set cancel to true on this object to cancel the close.
46830 "beforeclose": true,
46833 * Fires when this tab is closed.
46834 * @param {Roo.TabPanelItem} this
46838 * @event deactivate
46839 * Fires when this tab is no longer the active tab.
46840 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46841 * @param {Roo.TabPanelItem} this
46843 "deactivate" : true
46845 this.hidden = false;
46847 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46850 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46852 purgeListeners : function(){
46853 Roo.util.Observable.prototype.purgeListeners.call(this);
46854 this.el.removeAllListeners();
46857 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46860 this.status_node.addClass("active");
46863 this.tabPanel.stripWrap.repaint();
46865 this.fireEvent("activate", this.tabPanel, this);
46869 * Returns true if this tab is the active tab.
46870 * @return {Boolean}
46872 isActive : function(){
46873 return this.tabPanel.getActiveTab() == this;
46877 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46880 this.status_node.removeClass("active");
46882 this.fireEvent("deactivate", this.tabPanel, this);
46885 hideAction : function(){
46886 this.bodyEl.hide();
46887 this.bodyEl.setStyle("position", "absolute");
46888 this.bodyEl.setLeft("-20000px");
46889 this.bodyEl.setTop("-20000px");
46892 showAction : function(){
46893 this.bodyEl.setStyle("position", "relative");
46894 this.bodyEl.setTop("");
46895 this.bodyEl.setLeft("");
46896 this.bodyEl.show();
46900 * Set the tooltip for the tab.
46901 * @param {String} tooltip The tab's tooltip
46903 setTooltip : function(text){
46904 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46905 this.textEl.dom.qtip = text;
46906 this.textEl.dom.removeAttribute('title');
46908 this.textEl.dom.title = text;
46912 onTabClick : function(e){
46913 e.preventDefault();
46914 this.tabPanel.activate(this.id);
46917 onTabMouseDown : function(e){
46918 e.preventDefault();
46919 this.tabPanel.activate(this.id);
46922 getWidth : function(){
46923 return this.inner.getWidth();
46926 setWidth : function(width){
46927 var iwidth = width - this.linode.getPadding("lr");
46928 this.inner.setWidth(iwidth);
46929 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46930 this.linode.setWidth(width);
46934 * Show or hide the tab
46935 * @param {Boolean} hidden True to hide or false to show.
46937 setHidden : function(hidden){
46938 this.hidden = hidden;
46939 this.linode.setStyle("display", hidden ? "none" : "");
46943 * Returns true if this tab is "hidden"
46944 * @return {Boolean}
46946 isHidden : function(){
46947 return this.hidden;
46951 * Returns the text for this tab
46954 getText : function(){
46958 autoSize : function(){
46959 //this.el.beginMeasure();
46960 this.textEl.setWidth(1);
46962 * #2804 [new] Tabs in Roojs
46963 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46965 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46966 //this.el.endMeasure();
46970 * Sets the text for the tab (Note: this also sets the tooltip text)
46971 * @param {String} text The tab's text and tooltip
46973 setText : function(text){
46975 this.textEl.update(text);
46976 this.setTooltip(text);
46977 //if(!this.tabPanel.resizeTabs){
46978 // this.autoSize();
46982 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46984 activate : function(){
46985 this.tabPanel.activate(this.id);
46989 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46991 disable : function(){
46992 if(this.tabPanel.active != this){
46993 this.disabled = true;
46994 this.status_node.addClass("disabled");
46999 * Enables this TabPanelItem if it was previously disabled.
47001 enable : function(){
47002 this.disabled = false;
47003 this.status_node.removeClass("disabled");
47007 * Sets the content for this TabPanelItem.
47008 * @param {String} content The content
47009 * @param {Boolean} loadScripts true to look for and load scripts
47011 setContent : function(content, loadScripts){
47012 this.bodyEl.update(content, loadScripts);
47016 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47017 * @return {Roo.UpdateManager} The UpdateManager
47019 getUpdateManager : function(){
47020 return this.bodyEl.getUpdateManager();
47024 * Set a URL to be used to load the content for this TabPanelItem.
47025 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47026 * @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)
47027 * @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)
47028 * @return {Roo.UpdateManager} The UpdateManager
47030 setUrl : function(url, params, loadOnce){
47031 if(this.refreshDelegate){
47032 this.un('activate', this.refreshDelegate);
47034 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47035 this.on("activate", this.refreshDelegate);
47036 return this.bodyEl.getUpdateManager();
47040 _handleRefresh : function(url, params, loadOnce){
47041 if(!loadOnce || !this.loaded){
47042 var updater = this.bodyEl.getUpdateManager();
47043 updater.update(url, params, this._setLoaded.createDelegate(this));
47048 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
47049 * Will fail silently if the setUrl method has not been called.
47050 * This does not activate the panel, just updates its content.
47052 refresh : function(){
47053 if(this.refreshDelegate){
47054 this.loaded = false;
47055 this.refreshDelegate();
47060 _setLoaded : function(){
47061 this.loaded = true;
47065 closeClick : function(e){
47068 this.fireEvent("beforeclose", this, o);
47069 if(o.cancel !== true){
47070 this.tabPanel.removeTab(this.id);
47074 * The text displayed in the tooltip for the close icon.
47077 closeText : "Close this tab"
47080 * This script refer to:
47081 * Title: International Telephone Input
47082 * Author: Jack O'Connor
47083 * Code version: v12.1.12
47084 * Availability: https://github.com/jackocnr/intl-tel-input.git
47087 Roo.bootstrap.form.PhoneInputData = function() {
47090 "Afghanistan (افغانستان)",
47095 "Albania (Shqipëri)",
47100 "Algeria (الجزائر)",
47125 "Antigua and Barbuda",
47135 "Armenia (Հայաստան)",
47151 "Austria (Österreich)",
47156 "Azerbaijan (Azərbaycan)",
47166 "Bahrain (البحرين)",
47171 "Bangladesh (বাংলাদেশ)",
47181 "Belarus (Беларусь)",
47186 "Belgium (België)",
47216 "Bosnia and Herzegovina (Босна и Херцеговина)",
47231 "British Indian Ocean Territory",
47236 "British Virgin Islands",
47246 "Bulgaria (България)",
47256 "Burundi (Uburundi)",
47261 "Cambodia (កម្ពុជា)",
47266 "Cameroon (Cameroun)",
47275 ["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"]
47278 "Cape Verde (Kabu Verdi)",
47283 "Caribbean Netherlands",
47294 "Central African Republic (République centrafricaine)",
47314 "Christmas Island",
47320 "Cocos (Keeling) Islands",
47331 "Comoros (جزر القمر)",
47336 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47341 "Congo (Republic) (Congo-Brazzaville)",
47361 "Croatia (Hrvatska)",
47382 "Czech Republic (Česká republika)",
47387 "Denmark (Danmark)",
47402 "Dominican Republic (República Dominicana)",
47406 ["809", "829", "849"]
47424 "Equatorial Guinea (Guinea Ecuatorial)",
47444 "Falkland Islands (Islas Malvinas)",
47449 "Faroe Islands (Føroyar)",
47470 "French Guiana (Guyane française)",
47475 "French Polynesia (Polynésie française)",
47490 "Georgia (საქართველო)",
47495 "Germany (Deutschland)",
47515 "Greenland (Kalaallit Nunaat)",
47552 "Guinea-Bissau (Guiné Bissau)",
47577 "Hungary (Magyarország)",
47582 "Iceland (Ísland)",
47602 "Iraq (العراق)",
47618 "Israel (ישראל)",
47645 "Jordan (الأردن)",
47650 "Kazakhstan (Казахстан)",
47671 "Kuwait (الكويت)",
47676 "Kyrgyzstan (Кыргызстан)",
47686 "Latvia (Latvija)",
47691 "Lebanon (لبنان)",
47706 "Libya (ليبيا)",
47716 "Lithuania (Lietuva)",
47731 "Macedonia (FYROM) (Македонија)",
47736 "Madagascar (Madagasikara)",
47766 "Marshall Islands",
47776 "Mauritania (موريتانيا)",
47781 "Mauritius (Moris)",
47802 "Moldova (Republica Moldova)",
47812 "Mongolia (Монгол)",
47817 "Montenegro (Crna Gora)",
47827 "Morocco (المغرب)",
47833 "Mozambique (Moçambique)",
47838 "Myanmar (Burma) (မြန်မာ)",
47843 "Namibia (Namibië)",
47858 "Netherlands (Nederland)",
47863 "New Caledonia (Nouvelle-Calédonie)",
47898 "North Korea (조선 민주주의 인민 공화국)",
47903 "Northern Mariana Islands",
47919 "Pakistan (پاکستان)",
47929 "Palestine (فلسطين)",
47939 "Papua New Guinea",
47981 "Réunion (La Réunion)",
47987 "Romania (România)",
48003 "Saint Barthélemy",
48014 "Saint Kitts and Nevis",
48024 "Saint Martin (Saint-Martin (partie française))",
48030 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48035 "Saint Vincent and the Grenadines",
48050 "São Tomé and Príncipe (São Tomé e Príncipe)",
48055 "Saudi Arabia (المملكة العربية السعودية)",
48060 "Senegal (Sénégal)",
48090 "Slovakia (Slovensko)",
48095 "Slovenia (Slovenija)",
48105 "Somalia (Soomaaliya)",
48115 "South Korea (대한민국)",
48120 "South Sudan (جنوب السودان)",
48130 "Sri Lanka (ශ්රී ලංකාව)",
48135 "Sudan (السودان)",
48145 "Svalbard and Jan Mayen",
48156 "Sweden (Sverige)",
48161 "Switzerland (Schweiz)",
48166 "Syria (سوريا)",
48211 "Trinidad and Tobago",
48216 "Tunisia (تونس)",
48221 "Turkey (Türkiye)",
48231 "Turks and Caicos Islands",
48241 "U.S. Virgin Islands",
48251 "Ukraine (Україна)",
48256 "United Arab Emirates (الإمارات العربية المتحدة)",
48278 "Uzbekistan (Oʻzbekiston)",
48288 "Vatican City (Città del Vaticano)",
48299 "Vietnam (Việt Nam)",
48304 "Wallis and Futuna (Wallis-et-Futuna)",
48309 "Western Sahara (الصحراء الغربية)",
48315 "Yemen (اليمن)",
48339 * This script refer to:
48340 * Title: International Telephone Input
48341 * Author: Jack O'Connor
48342 * Code version: v12.1.12
48343 * Availability: https://github.com/jackocnr/intl-tel-input.git
48347 * @class Roo.bootstrap.form.PhoneInput
48348 * @extends Roo.bootstrap.form.TriggerField
48349 * An input with International dial-code selection
48351 * @cfg {String} defaultDialCode default '+852'
48352 * @cfg {Array} preferedCountries default []
48355 * Create a new PhoneInput.
48356 * @param {Object} config Configuration options
48359 Roo.bootstrap.form.PhoneInput = function(config) {
48360 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48363 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48365 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48367 listWidth: undefined,
48369 selectedClass: 'active',
48371 invalidClass : "has-warning",
48373 validClass: 'has-success',
48375 allowed: '0123456789',
48380 * @cfg {String} defaultDialCode The default dial code when initializing the input
48382 defaultDialCode: '+852',
48385 * @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
48387 preferedCountries: false,
48389 getAutoCreate : function()
48391 var data = Roo.bootstrap.form.PhoneInputData();
48392 var align = this.labelAlign || this.parentLabelAlign();
48395 this.allCountries = [];
48396 this.dialCodeMapping = [];
48398 for (var i = 0; i < data.length; i++) {
48400 this.allCountries[i] = {
48404 priority: c[3] || 0,
48405 areaCodes: c[4] || null
48407 this.dialCodeMapping[c[2]] = {
48410 priority: c[3] || 0,
48411 areaCodes: c[4] || null
48423 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48424 maxlength: this.max_length,
48425 cls : 'form-control tel-input',
48426 autocomplete: 'new-password'
48429 var hiddenInput = {
48432 cls: 'hidden-tel-input'
48436 hiddenInput.name = this.name;
48439 if (this.disabled) {
48440 input.disabled = true;
48443 var flag_container = {
48460 cls: this.hasFeedback ? 'has-feedback' : '',
48466 cls: 'dial-code-holder',
48473 cls: 'roo-select2-container input-group',
48480 if (this.fieldLabel.length) {
48483 tooltip: 'This field is required'
48489 cls: 'control-label',
48495 html: this.fieldLabel
48498 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48504 if(this.indicatorpos == 'right') {
48505 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48512 if(align == 'left') {
48520 if(this.labelWidth > 12){
48521 label.style = "width: " + this.labelWidth + 'px';
48523 if(this.labelWidth < 13 && this.labelmd == 0){
48524 this.labelmd = this.labelWidth;
48526 if(this.labellg > 0){
48527 label.cls += ' col-lg-' + this.labellg;
48528 input.cls += ' col-lg-' + (12 - this.labellg);
48530 if(this.labelmd > 0){
48531 label.cls += ' col-md-' + this.labelmd;
48532 container.cls += ' col-md-' + (12 - this.labelmd);
48534 if(this.labelsm > 0){
48535 label.cls += ' col-sm-' + this.labelsm;
48536 container.cls += ' col-sm-' + (12 - this.labelsm);
48538 if(this.labelxs > 0){
48539 label.cls += ' col-xs-' + this.labelxs;
48540 container.cls += ' col-xs-' + (12 - this.labelxs);
48550 var settings = this;
48552 ['xs','sm','md','lg'].map(function(size){
48553 if (settings[size]) {
48554 cfg.cls += ' col-' + size + '-' + settings[size];
48558 this.store = new Roo.data.Store({
48559 proxy : new Roo.data.MemoryProxy({}),
48560 reader : new Roo.data.JsonReader({
48571 'name' : 'dialCode',
48575 'name' : 'priority',
48579 'name' : 'areaCodes',
48586 if(!this.preferedCountries) {
48587 this.preferedCountries = [
48594 var p = this.preferedCountries.reverse();
48597 for (var i = 0; i < p.length; i++) {
48598 for (var j = 0; j < this.allCountries.length; j++) {
48599 if(this.allCountries[j].iso2 == p[i]) {
48600 var t = this.allCountries[j];
48601 this.allCountries.splice(j,1);
48602 this.allCountries.unshift(t);
48608 this.store.proxy.data = {
48610 data: this.allCountries
48616 initEvents : function()
48619 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48621 this.indicator = this.indicatorEl();
48622 this.flag = this.flagEl();
48623 this.dialCodeHolder = this.dialCodeHolderEl();
48625 this.trigger = this.el.select('div.flag-box',true).first();
48626 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48631 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48632 _this.list.setWidth(lw);
48635 this.list.on('mouseover', this.onViewOver, this);
48636 this.list.on('mousemove', this.onViewMove, this);
48637 this.inputEl().on("keyup", this.onKeyUp, this);
48638 this.inputEl().on("keypress", this.onKeyPress, this);
48640 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48642 this.view = new Roo.View(this.list, this.tpl, {
48643 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48646 this.view.on('click', this.onViewClick, this);
48647 this.setValue(this.defaultDialCode);
48650 onTriggerClick : function(e)
48652 Roo.log('trigger click');
48657 if(this.isExpanded()){
48659 this.hasFocus = false;
48661 this.store.load({});
48662 this.hasFocus = true;
48667 isExpanded : function()
48669 return this.list.isVisible();
48672 collapse : function()
48674 if(!this.isExpanded()){
48678 Roo.get(document).un('mousedown', this.collapseIf, this);
48679 Roo.get(document).un('mousewheel', this.collapseIf, this);
48680 this.fireEvent('collapse', this);
48684 expand : function()
48688 if(this.isExpanded() || !this.hasFocus){
48692 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48693 this.list.setWidth(lw);
48696 this.restrictHeight();
48698 Roo.get(document).on('mousedown', this.collapseIf, this);
48699 Roo.get(document).on('mousewheel', this.collapseIf, this);
48701 this.fireEvent('expand', this);
48704 restrictHeight : function()
48706 this.list.alignTo(this.inputEl(), this.listAlign);
48707 this.list.alignTo(this.inputEl(), this.listAlign);
48710 onViewOver : function(e, t)
48712 if(this.inKeyMode){
48715 var item = this.view.findItemFromChild(t);
48718 var index = this.view.indexOf(item);
48719 this.select(index, false);
48724 onViewClick : function(view, doFocus, el, e)
48726 var index = this.view.getSelectedIndexes()[0];
48728 var r = this.store.getAt(index);
48731 this.onSelect(r, index);
48733 if(doFocus !== false && !this.blockFocus){
48734 this.inputEl().focus();
48738 onViewMove : function(e, t)
48740 this.inKeyMode = false;
48743 select : function(index, scrollIntoView)
48745 this.selectedIndex = index;
48746 this.view.select(index);
48747 if(scrollIntoView !== false){
48748 var el = this.view.getNode(index);
48750 this.list.scrollChildIntoView(el, false);
48755 createList : function()
48757 this.list = Roo.get(document.body).createChild({
48759 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48760 style: 'display:none'
48763 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48766 collapseIf : function(e)
48768 var in_combo = e.within(this.el);
48769 var in_list = e.within(this.list);
48770 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48772 if (in_combo || in_list || is_list) {
48778 onSelect : function(record, index)
48780 if(this.fireEvent('beforeselect', this, record, index) !== false){
48782 this.setFlagClass(record.data.iso2);
48783 this.setDialCode(record.data.dialCode);
48784 this.hasFocus = false;
48786 this.fireEvent('select', this, record, index);
48790 flagEl : function()
48792 var flag = this.el.select('div.flag',true).first();
48799 dialCodeHolderEl : function()
48801 var d = this.el.select('input.dial-code-holder',true).first();
48808 setDialCode : function(v)
48810 this.dialCodeHolder.dom.value = '+'+v;
48813 setFlagClass : function(n)
48815 this.flag.dom.className = 'flag '+n;
48818 getValue : function()
48820 var v = this.inputEl().getValue();
48821 if(this.dialCodeHolder) {
48822 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48827 setValue : function(v)
48829 var d = this.getDialCode(v);
48831 //invalid dial code
48832 if(v.length == 0 || !d || d.length == 0) {
48834 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48835 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48841 this.setFlagClass(this.dialCodeMapping[d].iso2);
48842 this.setDialCode(d);
48843 this.inputEl().dom.value = v.replace('+'+d,'');
48844 this.hiddenEl().dom.value = this.getValue();
48849 getDialCode : function(v)
48853 if (v.length == 0) {
48854 return this.dialCodeHolder.dom.value;
48858 if (v.charAt(0) != "+") {
48861 var numericChars = "";
48862 for (var i = 1; i < v.length; i++) {
48863 var c = v.charAt(i);
48866 if (this.dialCodeMapping[numericChars]) {
48867 dialCode = v.substr(1, i);
48869 if (numericChars.length == 4) {
48879 this.setValue(this.defaultDialCode);
48883 hiddenEl : function()
48885 return this.el.select('input.hidden-tel-input',true).first();
48888 // after setting val
48889 onKeyUp : function(e){
48890 this.setValue(this.getValue());
48893 onKeyPress : function(e){
48894 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48901 * @class Roo.bootstrap.form.MoneyField
48902 * @extends Roo.bootstrap.form.ComboBox
48903 * Bootstrap MoneyField class
48906 * Create a new MoneyField.
48907 * @param {Object} config Configuration options
48910 Roo.bootstrap.form.MoneyField = function(config) {
48912 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48916 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48919 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48921 allowDecimals : true,
48923 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48925 decimalSeparator : ".",
48927 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48929 decimalPrecision : 0,
48931 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48933 allowNegative : true,
48935 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48939 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48941 minValue : Number.NEGATIVE_INFINITY,
48943 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48945 maxValue : Number.MAX_VALUE,
48947 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48949 minText : "The minimum value for this field is {0}",
48951 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48953 maxText : "The maximum value for this field is {0}",
48955 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48956 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48958 nanText : "{0} is not a valid number",
48960 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48964 * @cfg {String} defaults currency of the MoneyField
48965 * value should be in lkey
48967 defaultCurrency : false,
48969 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48971 thousandsDelimiter : false,
48973 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48982 * @cfg {Roo.data.Store} store Store to lookup currency??
48986 getAutoCreate : function()
48988 var align = this.labelAlign || this.parentLabelAlign();
49000 cls : 'form-control roo-money-amount-input',
49001 autocomplete: 'new-password'
49004 var hiddenInput = {
49008 cls: 'hidden-number-input'
49011 if(this.max_length) {
49012 input.maxlength = this.max_length;
49016 hiddenInput.name = this.name;
49019 if (this.disabled) {
49020 input.disabled = true;
49023 var clg = 12 - this.inputlg;
49024 var cmd = 12 - this.inputmd;
49025 var csm = 12 - this.inputsm;
49026 var cxs = 12 - this.inputxs;
49030 cls : 'row roo-money-field',
49034 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49038 cls: 'roo-select2-container input-group',
49042 cls : 'form-control roo-money-currency-input',
49043 autocomplete: 'new-password',
49045 name : this.currencyName
49049 cls : 'input-group-addon',
49063 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49067 cls: this.hasFeedback ? 'has-feedback' : '',
49078 if (this.fieldLabel.length) {
49081 tooltip: 'This field is required'
49087 cls: 'control-label',
49093 html: this.fieldLabel
49096 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49102 if(this.indicatorpos == 'right') {
49103 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49110 if(align == 'left') {
49118 if(this.labelWidth > 12){
49119 label.style = "width: " + this.labelWidth + 'px';
49121 if(this.labelWidth < 13 && this.labelmd == 0){
49122 this.labelmd = this.labelWidth;
49124 if(this.labellg > 0){
49125 label.cls += ' col-lg-' + this.labellg;
49126 input.cls += ' col-lg-' + (12 - this.labellg);
49128 if(this.labelmd > 0){
49129 label.cls += ' col-md-' + this.labelmd;
49130 container.cls += ' col-md-' + (12 - this.labelmd);
49132 if(this.labelsm > 0){
49133 label.cls += ' col-sm-' + this.labelsm;
49134 container.cls += ' col-sm-' + (12 - this.labelsm);
49136 if(this.labelxs > 0){
49137 label.cls += ' col-xs-' + this.labelxs;
49138 container.cls += ' col-xs-' + (12 - this.labelxs);
49149 var settings = this;
49151 ['xs','sm','md','lg'].map(function(size){
49152 if (settings[size]) {
49153 cfg.cls += ' col-' + size + '-' + settings[size];
49160 initEvents : function()
49162 this.indicator = this.indicatorEl();
49164 this.initCurrencyEvent();
49166 this.initNumberEvent();
49169 initCurrencyEvent : function()
49172 throw "can not find store for combo";
49175 this.store = Roo.factory(this.store, Roo.data);
49176 this.store.parent = this;
49180 this.triggerEl = this.el.select('.input-group-addon', true).first();
49182 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49187 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49188 _this.list.setWidth(lw);
49191 this.list.on('mouseover', this.onViewOver, this);
49192 this.list.on('mousemove', this.onViewMove, this);
49193 this.list.on('scroll', this.onViewScroll, this);
49196 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49199 this.view = new Roo.View(this.list, this.tpl, {
49200 singleSelect:true, store: this.store, selectedClass: this.selectedClass
49203 this.view.on('click', this.onViewClick, this);
49205 this.store.on('beforeload', this.onBeforeLoad, this);
49206 this.store.on('load', this.onLoad, this);
49207 this.store.on('loadexception', this.onLoadException, this);
49209 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49210 "up" : function(e){
49211 this.inKeyMode = true;
49215 "down" : function(e){
49216 if(!this.isExpanded()){
49217 this.onTriggerClick();
49219 this.inKeyMode = true;
49224 "enter" : function(e){
49227 if(this.fireEvent("specialkey", this, e)){
49228 this.onViewClick(false);
49234 "esc" : function(e){
49238 "tab" : function(e){
49241 if(this.fireEvent("specialkey", this, e)){
49242 this.onViewClick(false);
49250 doRelay : function(foo, bar, hname){
49251 if(hname == 'down' || this.scope.isExpanded()){
49252 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49260 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49264 initNumberEvent : function(e)
49266 this.inputEl().on("keydown" , this.fireKey, this);
49267 this.inputEl().on("focus", this.onFocus, this);
49268 this.inputEl().on("blur", this.onBlur, this);
49270 this.inputEl().relayEvent('keyup', this);
49272 if(this.indicator){
49273 this.indicator.addClass('invisible');
49276 this.originalValue = this.getValue();
49278 if(this.validationEvent == 'keyup'){
49279 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49280 this.inputEl().on('keyup', this.filterValidation, this);
49282 else if(this.validationEvent !== false){
49283 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49286 if(this.selectOnFocus){
49287 this.on("focus", this.preFocus, this);
49290 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49291 this.inputEl().on("keypress", this.filterKeys, this);
49293 this.inputEl().relayEvent('keypress', this);
49296 var allowed = "0123456789";
49298 if(this.allowDecimals){
49299 allowed += this.decimalSeparator;
49302 if(this.allowNegative){
49306 if(this.thousandsDelimiter) {
49310 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49312 var keyPress = function(e){
49314 var k = e.getKey();
49316 var c = e.getCharCode();
49319 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49320 allowed.indexOf(String.fromCharCode(c)) === -1
49326 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49330 if(allowed.indexOf(String.fromCharCode(c)) === -1){
49335 this.inputEl().on("keypress", keyPress, this);
49339 onTriggerClick : function(e)
49346 this.loadNext = false;
49348 if(this.isExpanded()){
49353 this.hasFocus = true;
49355 if(this.triggerAction == 'all') {
49356 this.doQuery(this.allQuery, true);
49360 this.doQuery(this.getRawValue());
49363 getCurrency : function()
49365 var v = this.currencyEl().getValue();
49370 restrictHeight : function()
49372 this.list.alignTo(this.currencyEl(), this.listAlign);
49373 this.list.alignTo(this.currencyEl(), this.listAlign);
49376 onViewClick : function(view, doFocus, el, e)
49378 var index = this.view.getSelectedIndexes()[0];
49380 var r = this.store.getAt(index);
49383 this.onSelect(r, index);
49387 onSelect : function(record, index){
49389 if(this.fireEvent('beforeselect', this, record, index) !== false){
49391 this.setFromCurrencyData(index > -1 ? record.data : false);
49395 this.fireEvent('select', this, record, index);
49399 setFromCurrencyData : function(o)
49403 this.lastCurrency = o;
49405 if (this.currencyField) {
49406 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49408 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
49411 this.lastSelectionText = currency;
49413 //setting default currency
49414 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49415 this.setCurrency(this.defaultCurrency);
49419 this.setCurrency(currency);
49422 setFromData : function(o)
49426 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49428 this.setFromCurrencyData(c);
49433 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49435 Roo.log('no value set for '+ (this.name ? this.name : this.id));
49438 this.setValue(value);
49442 setCurrency : function(v)
49444 this.currencyValue = v;
49447 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49452 setValue : function(v)
49454 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49460 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49462 this.inputEl().dom.value = (v == '') ? '' :
49463 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49465 if(!this.allowZero && v === '0') {
49466 this.hiddenEl().dom.value = '';
49467 this.inputEl().dom.value = '';
49474 getRawValue : function()
49476 var v = this.inputEl().getValue();
49481 getValue : function()
49483 return this.fixPrecision(this.parseValue(this.getRawValue()));
49486 parseValue : function(value)
49488 if(this.thousandsDelimiter) {
49490 r = new RegExp(",", "g");
49491 value = value.replace(r, "");
49494 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49495 return isNaN(value) ? '' : value;
49499 fixPrecision : function(value)
49501 if(this.thousandsDelimiter) {
49503 r = new RegExp(",", "g");
49504 value = value.replace(r, "");
49507 var nan = isNaN(value);
49509 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49510 return nan ? '' : value;
49512 return parseFloat(value).toFixed(this.decimalPrecision);
49515 decimalPrecisionFcn : function(v)
49517 return Math.floor(v);
49520 validateValue : function(value)
49522 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49526 var num = this.parseValue(value);
49529 this.markInvalid(String.format(this.nanText, value));
49533 if(num < this.minValue){
49534 this.markInvalid(String.format(this.minText, this.minValue));
49538 if(num > this.maxValue){
49539 this.markInvalid(String.format(this.maxText, this.maxValue));
49546 validate : function()
49548 if(this.disabled || this.allowBlank){
49553 var currency = this.getCurrency();
49555 if(this.validateValue(this.getRawValue()) && currency.length){
49560 this.markInvalid();
49564 getName: function()
49569 beforeBlur : function()
49575 var v = this.parseValue(this.getRawValue());
49582 onBlur : function()
49586 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49587 //this.el.removeClass(this.focusClass);
49590 this.hasFocus = false;
49592 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49596 var v = this.getValue();
49598 if(String(v) !== String(this.startValue)){
49599 this.fireEvent('change', this, v, this.startValue);
49602 this.fireEvent("blur", this);
49605 inputEl : function()
49607 return this.el.select('.roo-money-amount-input', true).first();
49610 currencyEl : function()
49612 return this.el.select('.roo-money-currency-input', true).first();
49615 hiddenEl : function()
49617 return this.el.select('input.hidden-number-input',true).first();
49621 * @class Roo.bootstrap.BezierSignature
49622 * @extends Roo.bootstrap.Component
49623 * Bootstrap BezierSignature class
49624 * This script refer to:
49625 * Title: Signature Pad
49627 * Availability: https://github.com/szimek/signature_pad
49630 * Create a new BezierSignature
49631 * @param {Object} config The config object
49634 Roo.bootstrap.BezierSignature = function(config){
49635 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49641 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49648 mouse_btn_down: true,
49651 * @cfg {int} canvas height
49653 canvas_height: '200px',
49656 * @cfg {float|function} Radius of a single dot.
49661 * @cfg {float} Minimum width of a line. Defaults to 0.5.
49666 * @cfg {float} Maximum width of a line. Defaults to 2.5.
49671 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49676 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49681 * @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.
49683 bg_color: 'rgba(0, 0, 0, 0)',
49686 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49688 dot_color: 'black',
49691 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49693 velocity_filter_weight: 0.7,
49696 * @cfg {function} Callback when stroke begin.
49701 * @cfg {function} Callback when stroke end.
49705 getAutoCreate : function()
49707 var cls = 'roo-signature column';
49710 cls += ' ' + this.cls;
49720 for(var i = 0; i < col_sizes.length; i++) {
49721 if(this[col_sizes[i]]) {
49722 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49732 cls: 'roo-signature-body',
49736 cls: 'roo-signature-body-canvas',
49737 height: this.canvas_height,
49738 width: this.canvas_width
49745 style: 'display: none'
49753 initEvents: function()
49755 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49757 var canvas = this.canvasEl();
49759 // mouse && touch event swapping...
49760 canvas.dom.style.touchAction = 'none';
49761 canvas.dom.style.msTouchAction = 'none';
49763 this.mouse_btn_down = false;
49764 canvas.on('mousedown', this._handleMouseDown, this);
49765 canvas.on('mousemove', this._handleMouseMove, this);
49766 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49768 if (window.PointerEvent) {
49769 canvas.on('pointerdown', this._handleMouseDown, this);
49770 canvas.on('pointermove', this._handleMouseMove, this);
49771 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49774 if ('ontouchstart' in window) {
49775 canvas.on('touchstart', this._handleTouchStart, this);
49776 canvas.on('touchmove', this._handleTouchMove, this);
49777 canvas.on('touchend', this._handleTouchEnd, this);
49780 Roo.EventManager.onWindowResize(this.resize, this, true);
49782 // file input event
49783 this.fileEl().on('change', this.uploadImage, this);
49790 resize: function(){
49792 var canvas = this.canvasEl().dom;
49793 var ctx = this.canvasElCtx();
49794 var img_data = false;
49796 if(canvas.width > 0) {
49797 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49799 // setting canvas width will clean img data
49802 var style = window.getComputedStyle ?
49803 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49805 var padding_left = parseInt(style.paddingLeft) || 0;
49806 var padding_right = parseInt(style.paddingRight) || 0;
49808 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49811 ctx.putImageData(img_data, 0, 0);
49815 _handleMouseDown: function(e)
49817 if (e.browserEvent.which === 1) {
49818 this.mouse_btn_down = true;
49819 this.strokeBegin(e);
49823 _handleMouseMove: function (e)
49825 if (this.mouse_btn_down) {
49826 this.strokeMoveUpdate(e);
49830 _handleMouseUp: function (e)
49832 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49833 this.mouse_btn_down = false;
49838 _handleTouchStart: function (e) {
49840 e.preventDefault();
49841 if (e.browserEvent.targetTouches.length === 1) {
49842 // var touch = e.browserEvent.changedTouches[0];
49843 // this.strokeBegin(touch);
49845 this.strokeBegin(e); // assume e catching the correct xy...
49849 _handleTouchMove: function (e) {
49850 e.preventDefault();
49851 // var touch = event.targetTouches[0];
49852 // _this._strokeMoveUpdate(touch);
49853 this.strokeMoveUpdate(e);
49856 _handleTouchEnd: function (e) {
49857 var wasCanvasTouched = e.target === this.canvasEl().dom;
49858 if (wasCanvasTouched) {
49859 e.preventDefault();
49860 // var touch = event.changedTouches[0];
49861 // _this._strokeEnd(touch);
49866 reset: function () {
49867 this._lastPoints = [];
49868 this._lastVelocity = 0;
49869 this._lastWidth = (this.min_width + this.max_width) / 2;
49870 this.canvasElCtx().fillStyle = this.dot_color;
49873 strokeMoveUpdate: function(e)
49875 this.strokeUpdate(e);
49877 if (this.throttle) {
49878 this.throttleStroke(this.strokeUpdate, this.throttle);
49881 this.strokeUpdate(e);
49885 strokeBegin: function(e)
49887 var newPointGroup = {
49888 color: this.dot_color,
49892 if (typeof this.onBegin === 'function') {
49896 this.curve_data.push(newPointGroup);
49898 this.strokeUpdate(e);
49901 strokeUpdate: function(e)
49903 var rect = this.canvasEl().dom.getBoundingClientRect();
49904 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49905 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49906 var lastPoints = lastPointGroup.points;
49907 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49908 var isLastPointTooClose = lastPoint
49909 ? point.distanceTo(lastPoint) <= this.min_distance
49911 var color = lastPointGroup.color;
49912 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49913 var curve = this.addPoint(point);
49915 this.drawDot({color: color, point: point});
49918 this.drawCurve({color: color, curve: curve});
49928 strokeEnd: function(e)
49930 this.strokeUpdate(e);
49931 if (typeof this.onEnd === 'function') {
49936 addPoint: function (point) {
49937 var _lastPoints = this._lastPoints;
49938 _lastPoints.push(point);
49939 if (_lastPoints.length > 2) {
49940 if (_lastPoints.length === 3) {
49941 _lastPoints.unshift(_lastPoints[0]);
49943 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49944 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49945 _lastPoints.shift();
49951 calculateCurveWidths: function (startPoint, endPoint) {
49952 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49953 (1 - this.velocity_filter_weight) * this._lastVelocity;
49955 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49958 start: this._lastWidth
49961 this._lastVelocity = velocity;
49962 this._lastWidth = newWidth;
49966 drawDot: function (_a) {
49967 var color = _a.color, point = _a.point;
49968 var ctx = this.canvasElCtx();
49969 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49971 this.drawCurveSegment(point.x, point.y, width);
49973 ctx.fillStyle = color;
49977 drawCurve: function (_a) {
49978 var color = _a.color, curve = _a.curve;
49979 var ctx = this.canvasElCtx();
49980 var widthDelta = curve.endWidth - curve.startWidth;
49981 var drawSteps = Math.floor(curve.length()) * 2;
49983 ctx.fillStyle = color;
49984 for (var i = 0; i < drawSteps; i += 1) {
49985 var t = i / drawSteps;
49991 var x = uuu * curve.startPoint.x;
49992 x += 3 * uu * t * curve.control1.x;
49993 x += 3 * u * tt * curve.control2.x;
49994 x += ttt * curve.endPoint.x;
49995 var y = uuu * curve.startPoint.y;
49996 y += 3 * uu * t * curve.control1.y;
49997 y += 3 * u * tt * curve.control2.y;
49998 y += ttt * curve.endPoint.y;
49999 var width = curve.startWidth + ttt * widthDelta;
50000 this.drawCurveSegment(x, y, width);
50006 drawCurveSegment: function (x, y, width) {
50007 var ctx = this.canvasElCtx();
50009 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50010 this.is_empty = false;
50015 var ctx = this.canvasElCtx();
50016 var canvas = this.canvasEl().dom;
50017 ctx.fillStyle = this.bg_color;
50018 ctx.clearRect(0, 0, canvas.width, canvas.height);
50019 ctx.fillRect(0, 0, canvas.width, canvas.height);
50020 this.curve_data = [];
50022 this.is_empty = true;
50027 return this.el.select('input',true).first();
50030 canvasEl: function()
50032 return this.el.select('canvas',true).first();
50035 canvasElCtx: function()
50037 return this.el.select('canvas',true).first().dom.getContext('2d');
50040 getImage: function(type)
50042 if(this.is_empty) {
50047 return this.canvasEl().dom.toDataURL('image/'+type, 1);
50050 drawFromImage: function(img_src)
50052 var img = new Image();
50054 img.onload = function(){
50055 this.canvasElCtx().drawImage(img, 0, 0);
50060 this.is_empty = false;
50063 selectImage: function()
50065 this.fileEl().dom.click();
50068 uploadImage: function(e)
50070 var reader = new FileReader();
50072 reader.onload = function(e){
50073 var img = new Image();
50074 img.onload = function(){
50076 this.canvasElCtx().drawImage(img, 0, 0);
50078 img.src = e.target.result;
50081 reader.readAsDataURL(e.target.files[0]);
50084 // Bezier Point Constructor
50085 Point: (function () {
50086 function Point(x, y, time) {
50089 this.time = time || Date.now();
50091 Point.prototype.distanceTo = function (start) {
50092 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50094 Point.prototype.equals = function (other) {
50095 return this.x === other.x && this.y === other.y && this.time === other.time;
50097 Point.prototype.velocityFrom = function (start) {
50098 return this.time !== start.time
50099 ? this.distanceTo(start) / (this.time - start.time)
50106 // Bezier Constructor
50107 Bezier: (function () {
50108 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50109 this.startPoint = startPoint;
50110 this.control2 = control2;
50111 this.control1 = control1;
50112 this.endPoint = endPoint;
50113 this.startWidth = startWidth;
50114 this.endWidth = endWidth;
50116 Bezier.fromPoints = function (points, widths, scope) {
50117 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50118 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50119 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50121 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50122 var dx1 = s1.x - s2.x;
50123 var dy1 = s1.y - s2.y;
50124 var dx2 = s2.x - s3.x;
50125 var dy2 = s2.y - s3.y;
50126 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50127 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50128 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50129 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50130 var dxm = m1.x - m2.x;
50131 var dym = m1.y - m2.y;
50132 var k = l2 / (l1 + l2);
50133 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50134 var tx = s2.x - cm.x;
50135 var ty = s2.y - cm.y;
50137 c1: new scope.Point(m1.x + tx, m1.y + ty),
50138 c2: new scope.Point(m2.x + tx, m2.y + ty)
50141 Bezier.prototype.length = function () {
50146 for (var i = 0; i <= steps; i += 1) {
50148 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50149 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50151 var xdiff = cx - px;
50152 var ydiff = cy - py;
50153 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50160 Bezier.prototype.point = function (t, start, c1, c2, end) {
50161 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50162 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50163 + (3.0 * c2 * (1.0 - t) * t * t)
50164 + (end * t * t * t);
50169 throttleStroke: function(fn, wait) {
50170 if (wait === void 0) { wait = 250; }
50172 var timeout = null;
50176 var later = function () {
50177 previous = Date.now();
50179 result = fn.apply(storedContext, storedArgs);
50181 storedContext = null;
50185 return function wrapper() {
50187 for (var _i = 0; _i < arguments.length; _i++) {
50188 args[_i] = arguments[_i];
50190 var now = Date.now();
50191 var remaining = wait - (now - previous);
50192 storedContext = this;
50194 if (remaining <= 0 || remaining > wait) {
50196 clearTimeout(timeout);
50200 result = fn.apply(storedContext, storedArgs);
50202 storedContext = null;
50206 else if (!timeout) {
50207 timeout = window.setTimeout(later, remaining);
50217 // old names for form elements
50218 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
50219 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
50220 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
50221 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
50222 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
50223 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
50224 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
50225 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
50226 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
50227 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
50228 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
50229 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
50230 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
50231 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
50232 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
50233 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
50234 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
50235 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
50236 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
50237 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
50238 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
50239 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
50240 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
50241 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
50242 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
50243 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
50245 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
50246 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50248 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
50249 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
50251 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
50252 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50253 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
50254 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator