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 Roo.log('ONRENDER: ' + this.xtype);
356 this.el = ct.createChild(cfg, position);
361 this.tooltipEl().attr('tooltip', this.tooltip);
364 if(this.tabIndex !== undefined){
365 this.el.dom.setAttribute('tabIndex', this.tabIndex);
372 * Fetch the element to add children to
373 * @return {Roo.Element} defaults to this.el
375 getChildContainer : function()
379 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
381 return Roo.get(document.body);
385 * Fetch the element to display the tooltip on.
386 * @return {Roo.Element} defaults to this.el
388 tooltipEl : function()
393 addxtype : function(tree,cntr)
397 cn = Roo.factory(tree);
398 //Roo.log(['addxtype', cn]);
400 cn.parentType = this.xtype; //??
401 cn.parentId = this.id;
403 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
404 if (typeof(cn.container_method) == 'string') {
405 cntr = cn.container_method;
409 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
411 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
413 var build_from_html = Roo.XComponent.build_from_html;
415 var is_body = (tree.xtype == 'Body') ;
417 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
419 var self_cntr_el = Roo.get(this[cntr](false));
421 // do not try and build conditional elements
422 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
426 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
427 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
428 return this.addxtypeChild(tree,cntr, is_body);
431 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
434 return this.addxtypeChild(Roo.apply({}, tree),cntr);
437 Roo.log('skipping render');
443 if (!build_from_html) {
447 // this i think handles overlaying multiple children of the same type
448 // with the sam eelement.. - which might be buggy..
450 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
456 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
460 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
467 addxtypeChild : function (tree, cntr, is_body)
469 Roo.debug && Roo.log('addxtypeChild:' + cntr);
471 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
474 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
475 (typeof(tree['flexy:foreach']) != 'undefined');
479 skip_children = false;
480 // render the element if it's not BODY.
483 // if parent was disabled, then do not try and create the children..
484 if(!this[cntr](true)){
489 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.
515 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
516 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
522 //echild.dom.removeAttribute('xtype');
524 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
525 Roo.debug && Roo.log(self_cntr_el);
526 Roo.debug && Roo.log(echild);
527 Roo.debug && Roo.log(cn);
533 // if object has flexy:if - then it may or may not be rendered.
534 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
535 // skip a flexy if element.
536 Roo.debug && Roo.log('skipping render');
537 Roo.debug && Roo.log(tree);
539 Roo.debug && Roo.log('skipping all children');
540 skip_children = true;
545 // actually if flexy:foreach is found, we really want to create
546 // multiple copies here...
548 //Roo.log(this[cntr]());
549 // some elements do not have render methods.. like the layouts...
551 if(this[cntr](true) === false){
556 cn.render && cn.render(this[cntr](true));
559 // then add the element..
566 if (typeof (tree.menu) != 'undefined') {
567 tree.menu.parentType = cn.xtype;
568 tree.menu.triggerEl = cn.el;
569 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
573 if (!tree.items || !tree.items.length) {
575 //Roo.log(["no children", this]);
580 var items = tree.items;
583 //Roo.log(items.length);
585 if (!skip_children) {
586 for(var i =0;i < items.length;i++) {
587 // Roo.log(['add child', items[i]]);
588 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
594 //Roo.log("fire childrenrendered");
596 cn.fireEvent('childrenrendered', this);
602 * Set the element that will be used to show or hide
604 setVisibilityEl : function(el)
606 this.visibilityEl = el;
610 * Get the element that will be used to show or hide
612 getVisibilityEl : function()
614 if (typeof(this.visibilityEl) == 'object') {
615 return this.visibilityEl;
618 if (typeof(this.visibilityEl) == 'string') {
619 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
626 * Show a component - removes 'hidden' class
630 if(!this.getVisibilityEl()){
634 this.getVisibilityEl().removeClass(['hidden','d-none']);
636 this.fireEvent('show', this);
641 * Hide a component - adds 'hidden' class
645 if(!this.getVisibilityEl()){
649 this.getVisibilityEl().addClass(['hidden','d-none']);
651 this.fireEvent('hide', this);
664 * @class Roo.bootstrap.Element
665 * @extends Roo.bootstrap.Component
666 * @children Roo.bootstrap.Component
667 * Bootstrap Element class (basically a DIV used to make random stuff )
669 * @cfg {String} html contents of the element
670 * @cfg {String} tag tag of the element
671 * @cfg {String} cls class of the element
672 * @cfg {Boolean} preventDefault (true|false) default false
673 * @cfg {Boolean} clickable (true|false) default false
674 * @cfg {String} role default blank - set to button to force cursor pointer
678 * Create a new Element
679 * @param {Object} config The config object
682 Roo.bootstrap.Element = function(config){
683 Roo.bootstrap.Element.superclass.constructor.call(this, config);
689 * When a element is chick
690 * @param {Roo.bootstrap.Element} this
691 * @param {Roo.EventObject} e
699 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
704 preventDefault: false,
709 getAutoCreate : function(){
713 // cls: this.cls, double assign in parent class Component.js :: onRender
716 if (this.role !== false) {
717 cfg.role = this.role;
723 initEvents: function()
725 Roo.bootstrap.Element.superclass.initEvents.call(this);
728 this.el.on('click', this.onClick, this);
734 onClick : function(e)
736 if(this.preventDefault){
740 this.fireEvent('click', this, e); // why was this double click before?
748 getValue : function()
750 return this.el.dom.innerHTML;
753 setValue : function(value)
755 this.el.dom.innerHTML = value;
770 * @class Roo.bootstrap.DropTarget
771 * @extends Roo.bootstrap.Element
772 * Bootstrap DropTarget class
774 * @cfg {string} name dropable name
777 * Create a new Dropable Area
778 * @param {Object} config The config object
781 Roo.bootstrap.DropTarget = function(config){
782 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
788 * When a element is chick
789 * @param {Roo.bootstrap.Element} this
790 * @param {Roo.EventObject} e
796 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
799 getAutoCreate : function(){
804 initEvents: function()
806 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
807 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
810 drop : this.dragDrop.createDelegate(this),
811 enter : this.dragEnter.createDelegate(this),
812 out : this.dragOut.createDelegate(this),
813 over : this.dragOver.createDelegate(this)
817 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
820 dragDrop : function(source,e,data)
822 // user has to decide how to impliment this.
825 //this.fireEvent('drop', this, source, e ,data);
829 dragEnter : function(n, dd, e, data)
831 // probably want to resize the element to match the dropped element..
833 this.originalSize = this.el.getSize();
834 this.el.setSize( n.el.getSize());
835 this.dropZone.DDM.refreshCache(this.name);
836 Roo.log([n, dd, e, data]);
839 dragOut : function(value)
841 // resize back to normal
843 this.el.setSize(this.originalSize);
844 this.dropZone.resetConstraints();
847 dragOver : function()
864 * @class Roo.bootstrap.Body
865 * @extends Roo.bootstrap.Component
866 * @children Roo.bootstrap.Component
867 * @parent none builder
868 * Bootstrap Body class
872 * @param {Object} config The config object
875 Roo.bootstrap.Body = function(config){
877 config = config || {};
879 Roo.bootstrap.Body.superclass.constructor.call(this, config);
880 this.el = Roo.get(config.el ? config.el : document.body );
881 if (this.cls && this.cls.length) {
882 Roo.get(document.body).addClass(this.cls);
886 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
888 is_body : true,// just to make sure it's constructed?
893 onRender : function(ct, position)
895 /* Roo.log("Roo.bootstrap.Body - onRender");
896 if (this.cls && this.cls.length) {
897 Roo.get(document.body).addClass(this.cls);
916 * @class Roo.bootstrap.ButtonGroup
917 * @extends Roo.bootstrap.Component
918 * Bootstrap ButtonGroup class
919 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
921 * @cfg {String} size lg | sm | xs (default empty normal)
922 * @cfg {String} align vertical | justified (default none)
923 * @cfg {String} direction up | down (default down)
924 * @cfg {Boolean} toolbar false | true
925 * @cfg {Boolean} btn true | false
930 * @param {Object} config The config object
933 Roo.bootstrap.ButtonGroup = function(config){
934 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
937 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
945 getAutoCreate : function(){
951 cfg.html = this.html || cfg.html;
962 if (['vertical','justified'].indexOf(this.align)!==-1) {
963 cfg.cls = 'btn-group-' + this.align;
965 if (this.align == 'justified') {
966 console.log(this.items);
970 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
971 cfg.cls += ' btn-group-' + this.size;
974 if (this.direction == 'up') {
975 cfg.cls += ' dropup' ;
981 * Add a button to the group (similar to NavItem API.)
983 addItem : function(cfg)
985 var cn = new Roo.bootstrap.Button(cfg);
987 cn.parentId = this.id;
988 cn.onRender(this.el, null);
1002 * @class Roo.bootstrap.Button
1003 * @extends Roo.bootstrap.Component
1004 * Bootstrap Button class
1005 * @cfg {String} html The button content
1006 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1007 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1008 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1009 * @cfg {String} size (lg|sm|xs)
1010 * @cfg {String} tag (a|input|submit)
1011 * @cfg {String} href empty or href
1012 * @cfg {Boolean} disabled default false;
1013 * @cfg {Boolean} isClose default false;
1014 * @cfg {String} glyphicon depricated - use fa
1015 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1016 * @cfg {String} badge text for badge
1017 * @cfg {String} theme (default|glow)
1018 * @cfg {Boolean} inverse dark themed version
1019 * @cfg {Boolean} toggle is it a slidy toggle button
1020 * @cfg {Boolean} pressed default null - if the button ahs active state
1021 * @cfg {String} ontext text for on slidy toggle state
1022 * @cfg {String} offtext text for off slidy toggle state
1023 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1024 * @cfg {Boolean} removeClass remove the standard class..
1025 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1026 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1027 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1030 * Create a new button
1031 * @param {Object} config The config object
1035 Roo.bootstrap.Button = function(config){
1036 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1042 * When a button is pressed
1043 * @param {Roo.bootstrap.Button} btn
1044 * @param {Roo.EventObject} e
1049 * When a button is double clicked
1050 * @param {Roo.bootstrap.Button} btn
1051 * @param {Roo.EventObject} e
1056 * After the button has been toggles
1057 * @param {Roo.bootstrap.Button} btn
1058 * @param {Roo.EventObject} e
1059 * @param {boolean} pressed (also available as button.pressed)
1065 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1086 preventDefault: true,
1095 getAutoCreate : function(){
1103 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1104 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1105 this.tag = 'button';
1109 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1111 if (this.toggle == true) {
1114 cls: 'slider-frame roo-button',
1118 'data-on-text':'ON',
1119 'data-off-text':'OFF',
1120 cls: 'slider-button',
1125 // why are we validating the weights?
1126 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1127 cfg.cls += ' ' + this.weight;
1134 cfg.cls += ' close';
1136 cfg["aria-hidden"] = true;
1138 cfg.html = "×";
1144 if (this.theme==='default') {
1145 cfg.cls = 'btn roo-button';
1147 //if (this.parentType != 'Navbar') {
1148 this.weight = this.weight.length ? this.weight : 'default';
1150 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1152 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1153 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1154 cfg.cls += ' btn-' + outline + weight;
1155 if (this.weight == 'default') {
1157 cfg.cls += ' btn-' + this.weight;
1160 } else if (this.theme==='glow') {
1163 cfg.cls = 'btn-glow roo-button';
1165 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1167 cfg.cls += ' ' + this.weight;
1173 this.cls += ' inverse';
1177 if (this.active || this.pressed === true) {
1178 cfg.cls += ' active';
1181 if (this.disabled) {
1182 cfg.disabled = 'disabled';
1186 Roo.log('changing to ul' );
1188 this.glyphicon = 'caret';
1189 if (Roo.bootstrap.version == 4) {
1190 this.fa = 'caret-down';
1195 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1197 //gsRoo.log(this.parentType);
1198 if (this.parentType === 'Navbar' && !this.parent().bar) {
1199 Roo.log('changing to li?');
1208 href : this.href || '#'
1211 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1212 cfg.cls += ' dropdown';
1219 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1221 if (this.glyphicon) {
1222 cfg.html = ' ' + cfg.html;
1227 cls: 'glyphicon glyphicon-' + this.glyphicon
1232 cfg.html = ' ' + cfg.html;
1237 cls: 'fa fas fa-' + this.fa
1247 // cfg.cls='btn roo-button';
1251 var value = cfg.html;
1256 cls: 'glyphicon glyphicon-' + this.glyphicon,
1263 cls: 'fa fas fa-' + this.fa,
1268 var bw = this.badge_weight.length ? this.badge_weight :
1269 (this.weight.length ? this.weight : 'secondary');
1270 bw = bw == 'default' ? 'secondary' : bw;
1276 cls: 'badge badge-' + bw,
1285 cfg.cls += ' dropdown';
1286 cfg.html = typeof(cfg.html) != 'undefined' ?
1287 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1290 if (cfg.tag !== 'a' && this.href !== '') {
1291 throw "Tag must be a to set href.";
1292 } else if (this.href.length > 0) {
1293 cfg.href = this.href;
1296 if(this.removeClass){
1301 cfg.target = this.target;
1306 initEvents: function() {
1307 // Roo.log('init events?');
1308 // Roo.log(this.el.dom);
1311 if (typeof (this.menu) != 'undefined') {
1312 this.menu.parentType = this.xtype;
1313 this.menu.triggerEl = this.el;
1314 this.addxtype(Roo.apply({}, this.menu));
1318 if (this.el.hasClass('roo-button')) {
1319 this.el.on('click', this.onClick, this);
1320 this.el.on('dblclick', this.onDblClick, this);
1322 this.el.select('.roo-button').on('click', this.onClick, this);
1323 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1327 if(this.removeClass){
1328 this.el.on('click', this.onClick, this);
1331 if (this.group === true) {
1332 if (this.pressed === false || this.pressed === true) {
1335 this.pressed = false;
1336 this.setActive(this.pressed);
1341 this.el.enableDisplayMode();
1344 onClick : function(e)
1346 if (this.disabled) {
1350 Roo.log('button on click ');
1351 if(this.href === '' || this.preventDefault){
1360 this.setActive(true);
1361 var pi = this.parent().items;
1362 for (var i = 0;i < pi.length;i++) {
1363 if (this == pi[i]) {
1366 if (pi[i].el.hasClass('roo-button')) {
1367 pi[i].setActive(false);
1370 this.fireEvent('click', this, e);
1374 if (this.pressed === true || this.pressed === false) {
1375 this.toggleActive(e);
1379 this.fireEvent('click', this, e);
1381 onDblClick: function(e)
1383 if (this.disabled) {
1386 if(this.preventDefault){
1389 this.fireEvent('dblclick', this, e);
1392 * Enables this button
1396 this.disabled = false;
1397 this.el.removeClass('disabled');
1398 this.el.dom.removeAttribute("disabled");
1402 * Disable this button
1404 disable : function()
1406 this.disabled = true;
1407 this.el.addClass('disabled');
1408 this.el.attr("disabled", "disabled")
1411 * sets the active state on/off,
1412 * @param {Boolean} state (optional) Force a particular state
1414 setActive : function(v) {
1416 this.el[v ? 'addClass' : 'removeClass']('active');
1420 * toggles the current active state
1422 toggleActive : function(e)
1424 this.setActive(!this.pressed); // this modifies pressed...
1425 this.fireEvent('toggle', this, e, this.pressed);
1428 * get the current active state
1429 * @return {boolean} true if it's active
1431 isActive : function()
1433 return this.el.hasClass('active');
1436 * set the text of the first selected button
1438 setText : function(str)
1440 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1443 * get the text of the first selected button
1445 getText : function()
1447 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1450 setWeight : function(str)
1452 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1453 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1455 var outline = this.outline ? 'outline-' : '';
1456 if (str == 'default') {
1457 this.el.addClass('btn-default btn-outline-secondary');
1460 this.el.addClass('btn-' + outline + str);
1465 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1467 Roo.bootstrap.Button.weights = [
1487 * @class Roo.bootstrap.Column
1488 * @extends Roo.bootstrap.Component
1489 * @children Roo.bootstrap.Component
1490 * Bootstrap Column class
1491 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1492 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1493 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1494 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1495 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1496 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1497 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1498 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1501 * @cfg {Boolean} hidden (true|false) hide the element
1502 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1503 * @cfg {String} fa (ban|check|...) font awesome icon
1504 * @cfg {Number} fasize (1|2|....) font awsome size
1506 * @cfg {String} icon (info-sign|check|...) glyphicon name
1508 * @cfg {String} html content of column.
1511 * Create a new Column
1512 * @param {Object} config The config object
1515 Roo.bootstrap.Column = function(config){
1516 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1519 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1537 getAutoCreate : function(){
1538 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1546 var sizes = ['xs','sm','md','lg'];
1547 sizes.map(function(size ,ix){
1548 //Roo.log( size + ':' + settings[size]);
1550 if (settings[size+'off'] !== false) {
1551 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1554 if (settings[size] === false) {
1558 if (!settings[size]) { // 0 = hidden
1559 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1561 for (var i = ix; i > -1; i--) {
1562 cfg.cls += ' d-' + sizes[i] + '-none';
1568 cfg.cls += ' col-' + size + '-' + settings[size] + (
1569 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1575 cfg.cls += ' hidden';
1578 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1579 cfg.cls +=' alert alert-' + this.alert;
1583 if (this.html.length) {
1584 cfg.html = this.html;
1588 if (this.fasize > 1) {
1589 fasize = ' fa-' + this.fasize + 'x';
1591 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1596 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1615 * @class Roo.bootstrap.Container
1616 * @extends Roo.bootstrap.Component
1617 * @children Roo.bootstrap.Component
1619 * Bootstrap Container class
1620 * @cfg {Boolean} jumbotron is it a jumbotron element
1621 * @cfg {String} html content of element
1622 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1623 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1624 * @cfg {String} header content of header (for panel)
1625 * @cfg {String} footer content of footer (for panel)
1626 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1627 * @cfg {String} tag (header|aside|section) type of HTML tag.
1628 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1629 * @cfg {String} fa font awesome icon
1630 * @cfg {String} icon (info-sign|check|...) glyphicon name
1631 * @cfg {Boolean} hidden (true|false) hide the element
1632 * @cfg {Boolean} expandable (true|false) default false
1633 * @cfg {Boolean} expanded (true|false) default true
1634 * @cfg {String} rheader contet on the right of header
1635 * @cfg {Boolean} clickable (true|false) default false
1639 * Create a new Container
1640 * @param {Object} config The config object
1643 Roo.bootstrap.Container = function(config){
1644 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1650 * After the panel has been expand
1652 * @param {Roo.bootstrap.Container} this
1657 * After the panel has been collapsed
1659 * @param {Roo.bootstrap.Container} this
1664 * When a element is chick
1665 * @param {Roo.bootstrap.Container} this
1666 * @param {Roo.EventObject} e
1672 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1690 getChildContainer : function() {
1696 if (this.panel.length) {
1697 return this.el.select('.panel-body',true).first();
1704 getAutoCreate : function(){
1707 tag : this.tag || 'div',
1711 if (this.jumbotron) {
1712 cfg.cls = 'jumbotron';
1717 // - this is applied by the parent..
1719 // cfg.cls = this.cls + '';
1722 if (this.sticky.length) {
1724 var bd = Roo.get(document.body);
1725 if (!bd.hasClass('bootstrap-sticky')) {
1726 bd.addClass('bootstrap-sticky');
1727 Roo.select('html',true).setStyle('height', '100%');
1730 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1734 if (this.well.length) {
1735 switch (this.well) {
1738 cfg.cls +=' well well-' +this.well;
1747 cfg.cls += ' hidden';
1751 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1752 cfg.cls +=' alert alert-' + this.alert;
1757 if (this.panel.length) {
1758 cfg.cls += ' panel panel-' + this.panel;
1760 if (this.header.length) {
1764 if(this.expandable){
1766 cfg.cls = cfg.cls + ' expandable';
1770 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1778 cls : 'panel-title',
1779 html : (this.expandable ? ' ' : '') + this.header
1783 cls: 'panel-header-right',
1789 cls : 'panel-heading',
1790 style : this.expandable ? 'cursor: pointer' : '',
1798 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1803 if (this.footer.length) {
1805 cls : 'panel-footer',
1814 body.html = this.html || cfg.html;
1815 // prefix with the icons..
1817 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1820 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1825 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1826 cfg.cls = 'container';
1832 initEvents: function()
1834 if(this.expandable){
1835 var headerEl = this.headerEl();
1838 headerEl.on('click', this.onToggleClick, this);
1843 this.el.on('click', this.onClick, this);
1848 onToggleClick : function()
1850 var headerEl = this.headerEl();
1866 if(this.fireEvent('expand', this)) {
1868 this.expanded = true;
1870 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1872 this.el.select('.panel-body',true).first().removeClass('hide');
1874 var toggleEl = this.toggleEl();
1880 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1885 collapse : function()
1887 if(this.fireEvent('collapse', this)) {
1889 this.expanded = false;
1891 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1892 this.el.select('.panel-body',true).first().addClass('hide');
1894 var toggleEl = this.toggleEl();
1900 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1904 toggleEl : function()
1906 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1910 return this.el.select('.panel-heading .fa',true).first();
1913 headerEl : function()
1915 if(!this.el || !this.panel.length || !this.header.length){
1919 return this.el.select('.panel-heading',true).first()
1924 if(!this.el || !this.panel.length){
1928 return this.el.select('.panel-body',true).first()
1931 titleEl : function()
1933 if(!this.el || !this.panel.length || !this.header.length){
1937 return this.el.select('.panel-title',true).first();
1940 setTitle : function(v)
1942 var titleEl = this.titleEl();
1948 titleEl.dom.innerHTML = v;
1951 getTitle : function()
1954 var titleEl = this.titleEl();
1960 return titleEl.dom.innerHTML;
1963 setRightTitle : function(v)
1965 var t = this.el.select('.panel-header-right',true).first();
1971 t.dom.innerHTML = v;
1974 onClick : function(e)
1978 this.fireEvent('click', this, e);
1983 * @class Roo.bootstrap.Card
1984 * @extends Roo.bootstrap.Component
1985 * @children Roo.bootstrap.Component
1987 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1990 * possible... may not be implemented..
1991 * @cfg {String} header_image src url of image.
1992 * @cfg {String|Object} header
1993 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1994 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1996 * @cfg {String} title
1997 * @cfg {String} subtitle
1998 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1999 * @cfg {String} footer
2001 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
2003 * @cfg {String} margin (0|1|2|3|4|5|auto)
2004 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2005 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2006 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2007 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2008 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2009 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2011 * @cfg {String} padding (0|1|2|3|4|5)
2012 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2013 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2014 * @cfg {String} padding_left (0|1|2|3|4|5)
2015 * @cfg {String} padding_right (0|1|2|3|4|5)
2016 * @cfg {String} padding_x (0|1|2|3|4|5)
2017 * @cfg {String} padding_y (0|1|2|3|4|5)
2019 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2020 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2021 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2022 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2023 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2025 * @config {Boolean} dragable if this card can be dragged.
2026 * @config {String} drag_group group for drag
2027 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2028 * @config {String} drop_group group for drag
2030 * @config {Boolean} collapsable can the body be collapsed.
2031 * @config {Boolean} collapsed is the body collapsed when rendered...
2032 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2033 * @config {Boolean} rotated is the body rotated when rendered...
2036 * Create a new Container
2037 * @param {Object} config The config object
2040 Roo.bootstrap.Card = function(config){
2041 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2047 * When a element a card is dropped
2048 * @param {Roo.bootstrap.Card} this
2051 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2052 * @param {String} position 'above' or 'below'
2053 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2059 * When a element a card is rotate
2060 * @param {Roo.bootstrap.Card} this
2061 * @param {Roo.Element} n the node being dropped?
2062 * @param {Boolean} rotate status
2067 * When a card element is dragged over ready to drop (return false to block dropable)
2068 * @param {Roo.bootstrap.Card} this
2069 * @param {Object} data from dragdrop
2077 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2082 margin: '', /// may be better in component?
2112 collapsable : false,
2121 childContainer : false,
2122 dropEl : false, /// the dom placeholde element that indicates drop location.
2123 containerEl: false, // body container
2124 bodyEl: false, // card-body
2125 headerContainerEl : false, //
2127 header_imageEl : false,
2130 layoutCls : function()
2134 Roo.log(this.margin_bottom.length);
2135 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2136 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2138 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2139 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2141 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2142 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2146 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2147 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2148 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2152 // more generic support?
2160 // Roo.log("Call onRender: " + this.xtype);
2161 /* We are looking at something like this.
2163 <img src="..." class="card-img-top" alt="...">
2164 <div class="card-body">
2165 <h5 class="card-title">Card title</h5>
2166 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2168 >> this bit is really the body...
2169 <div> << we will ad dthis in hopefully it will not break shit.
2171 ** card text does not actually have any styling...
2173 <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>
2176 <a href="#" class="card-link">Card link</a>
2179 <div class="card-footer">
2180 <small class="text-muted">Last updated 3 mins ago</small>
2184 getAutoCreate : function(){
2192 if (this.weight.length && this.weight != 'light') {
2193 cfg.cls += ' text-white';
2195 cfg.cls += ' text-dark'; // need as it's nested..
2197 if (this.weight.length) {
2198 cfg.cls += ' bg-' + this.weight;
2201 cfg.cls += ' ' + this.layoutCls();
2204 var hdr_ctr = false;
2205 if (this.header.length) {
2207 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2208 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2216 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2222 if (this.collapsable) {
2225 cls : 'd-block user-select-none',
2229 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2234 hdr.cn.push(hdr_ctr);
2239 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2244 if (this.header_image.length) {
2247 cls : 'card-img-top',
2248 src: this.header_image // escape?
2253 cls : 'card-img-top d-none'
2259 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2263 if (this.collapsable || this.rotateable) {
2266 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2273 if (this.title.length) {
2277 src: this.title // escape?
2281 if (this.subtitle.length) {
2285 src: this.subtitle // escape?
2291 cls : 'roo-card-body-ctr'
2294 if (this.html.length) {
2300 // fixme ? handle objects?
2302 if (this.footer.length) {
2305 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2310 cfg.cn.push({cls : 'card-footer d-none'});
2319 getCardHeader : function()
2321 var ret = this.el.select('.card-header',true).first();
2322 if (ret.hasClass('d-none')) {
2323 ret.removeClass('d-none');
2328 getCardFooter : function()
2330 var ret = this.el.select('.card-footer',true).first();
2331 if (ret.hasClass('d-none')) {
2332 ret.removeClass('d-none');
2337 getCardImageTop : function()
2339 var ret = this.header_imageEl;
2340 if (ret.hasClass('d-none')) {
2341 ret.removeClass('d-none');
2347 getChildContainer : function()
2353 return this.el.select('.roo-card-body-ctr',true).first();
2356 initEvents: function()
2358 this.bodyEl = this.el.select('.card-body',true).first();
2359 this.containerEl = this.getChildContainer();
2361 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2362 containerScroll: true,
2363 ddGroup: this.drag_group || 'default_card_drag_group'
2365 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2367 if (this.dropable) {
2368 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2369 containerScroll: true,
2370 ddGroup: this.drop_group || 'default_card_drag_group'
2372 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2373 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2374 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2375 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2376 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2379 if (this.collapsable) {
2380 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2382 if (this.rotateable) {
2383 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2385 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2387 this.footerEl = this.el.select('.card-footer',true).first();
2388 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2389 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2390 this.headerEl = this.el.select('.card-header',true).first();
2393 this.el.addClass('roo-card-rotated');
2394 this.fireEvent('rotate', this, true);
2396 this.header_imageEl = this.el.select('.card-img-top',true).first();
2397 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2400 getDragData : function(e)
2402 var target = this.getEl();
2404 //this.handleSelection(e);
2409 nodes: this.getEl(),
2414 dragData.ddel = target.dom ; // the div element
2415 Roo.log(target.getWidth( ));
2416 dragData.ddel.style.width = target.getWidth() + 'px';
2423 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2424 * whole Element becomes the target, and this causes the drop gesture to append.
2426 * Returns an object:
2429 position : 'below' or 'above'
2430 card : relateive to card OBJECT (or true for no cards listed)
2431 items_n : relative to nth item in list
2432 card_n : relative to nth card in list
2437 getTargetFromEvent : function(e, dragged_card_el)
2439 var target = e.getTarget();
2440 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2441 target = target.parentNode;
2452 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2453 // see if target is one of the 'cards'...
2456 //Roo.log(this.items.length);
2459 var last_card_n = 0;
2461 for (var i = 0;i< this.items.length;i++) {
2463 if (!this.items[i].el.hasClass('card')) {
2466 pos = this.getDropPoint(e, this.items[i].el.dom);
2468 cards_len = ret.cards.length;
2469 //Roo.log(this.items[i].el.dom.id);
2470 ret.cards.push(this.items[i]);
2472 if (ret.card_n < 0 && pos == 'above') {
2473 ret.position = cards_len > 0 ? 'below' : pos;
2474 ret.items_n = i > 0 ? i - 1 : 0;
2475 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2476 ret.card = ret.cards[ret.card_n];
2479 if (!ret.cards.length) {
2481 ret.position = 'below';
2485 // could not find a card.. stick it at the end..
2486 if (ret.card_n < 0) {
2487 ret.card_n = last_card_n;
2488 ret.card = ret.cards[last_card_n];
2489 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2490 ret.position = 'below';
2493 if (this.items[ret.items_n].el == dragged_card_el) {
2497 if (ret.position == 'below') {
2498 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2500 if (card_after && card_after.el == dragged_card_el) {
2507 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2509 if (card_before && card_before.el == dragged_card_el) {
2516 onNodeEnter : function(n, dd, e, data){
2519 onNodeOver : function(n, dd, e, data)
2522 var target_info = this.getTargetFromEvent(e,data.source.el);
2523 if (target_info === false) {
2524 this.dropPlaceHolder('hide');
2527 Roo.log(['getTargetFromEvent', target_info ]);
2530 if (this.fireEvent('cardover', this, [ data ]) === false) {
2534 this.dropPlaceHolder('show', target_info,data);
2538 onNodeOut : function(n, dd, e, data){
2539 this.dropPlaceHolder('hide');
2542 onNodeDrop : function(n, dd, e, data)
2545 // call drop - return false if
2547 // this could actually fail - if the Network drops..
2548 // we will ignore this at present..- client should probably reload
2549 // the whole set of cards if stuff like that fails.
2552 var info = this.getTargetFromEvent(e,data.source.el);
2553 if (info === false) {
2556 this.dropPlaceHolder('hide');
2560 this.acceptCard(data.source, info.position, info.card, info.items_n);
2564 firstChildCard : function()
2566 for (var i = 0;i< this.items.length;i++) {
2568 if (!this.items[i].el.hasClass('card')) {
2571 return this.items[i];
2573 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2578 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2580 acceptCard : function(move_card, position, next_to_card )
2582 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2586 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2588 move_card.parent().removeCard(move_card);
2591 var dom = move_card.el.dom;
2592 dom.style.width = ''; // clear with - which is set by drag.
2594 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2595 var cardel = next_to_card.el.dom;
2597 if (position == 'above' ) {
2598 cardel.parentNode.insertBefore(dom, cardel);
2599 } else if (cardel.nextSibling) {
2600 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2602 cardel.parentNode.append(dom);
2605 // card container???
2606 this.containerEl.dom.append(dom);
2609 //FIXME HANDLE card = true
2611 // add this to the correct place in items.
2613 // remove Card from items.
2616 if (this.items.length) {
2618 //Roo.log([info.items_n, info.position, this.items.length]);
2619 for (var i =0; i < this.items.length; i++) {
2620 if (i == to_items_n && position == 'above') {
2621 nitems.push(move_card);
2623 nitems.push(this.items[i]);
2624 if (i == to_items_n && position == 'below') {
2625 nitems.push(move_card);
2628 this.items = nitems;
2629 Roo.log(this.items);
2631 this.items.push(move_card);
2634 move_card.parentId = this.id;
2640 removeCard : function(c)
2642 this.items = this.items.filter(function(e) { return e != c });
2645 dom.parentNode.removeChild(dom);
2646 dom.style.width = ''; // clear with - which is set by drag.
2651 /** Decide whether to drop above or below a View node. */
2652 getDropPoint : function(e, n, dd)
2657 if (n == this.containerEl.dom) {
2660 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2661 var c = t + (b - t) / 2;
2662 var y = Roo.lib.Event.getPageY(e);
2669 onToggleCollapse : function(e)
2671 if (this.collapsed) {
2672 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2673 this.collapsableEl.addClass('show');
2674 this.collapsed = false;
2677 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2678 this.collapsableEl.removeClass('show');
2679 this.collapsed = true;
2684 onToggleRotate : function(e)
2686 this.collapsableEl.removeClass('show');
2687 this.footerEl.removeClass('d-none');
2688 this.el.removeClass('roo-card-rotated');
2689 this.el.removeClass('d-none');
2692 this.collapsableEl.addClass('show');
2693 this.rotated = false;
2694 this.fireEvent('rotate', this, this.rotated);
2697 this.el.addClass('roo-card-rotated');
2698 this.footerEl.addClass('d-none');
2699 this.el.select('.roo-collapsable').removeClass('show');
2701 this.rotated = true;
2702 this.fireEvent('rotate', this, this.rotated);
2706 dropPlaceHolder: function (action, info, data)
2708 if (this.dropEl === false) {
2709 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2713 this.dropEl.removeClass(['d-none', 'd-block']);
2714 if (action == 'hide') {
2716 this.dropEl.addClass('d-none');
2719 // FIXME - info.card == true!!!
2720 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2722 if (info.card !== true) {
2723 var cardel = info.card.el.dom;
2725 if (info.position == 'above') {
2726 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2727 } else if (cardel.nextSibling) {
2728 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2730 cardel.parentNode.append(this.dropEl.dom);
2733 // card container???
2734 this.containerEl.dom.append(this.dropEl.dom);
2737 this.dropEl.addClass('d-block roo-card-dropzone');
2739 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2746 setHeaderText: function(html)
2749 if (this.headerContainerEl) {
2750 this.headerContainerEl.dom.innerHTML = html;
2753 onHeaderImageLoad : function(ev, he)
2755 if (!this.header_image_fit_square) {
2759 var hw = he.naturalHeight / he.naturalWidth;
2762 //var w = he.dom.naturalWidth;
2765 he.style.position = 'relative';
2767 var nw = (ww * (1/hw));
2768 Roo.get(he).setSize( ww * (1/hw), ww);
2769 he.style.left = ((ww - nw)/ 2) + 'px';
2770 he.style.position = 'relative';
2781 * Card header - holder for the card header elements.
2786 * @class Roo.bootstrap.CardHeader
2787 * @extends Roo.bootstrap.Element
2788 * @parent Roo.bootstrap.Card
2789 * @children Roo.bootstrap.Component
2790 * Bootstrap CardHeader class
2792 * Create a new Card Header - that you can embed children into
2793 * @param {Object} config The config object
2796 Roo.bootstrap.CardHeader = function(config){
2797 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2800 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2803 container_method : 'getCardHeader'
2816 * Card footer - holder for the card footer elements.
2821 * @class Roo.bootstrap.CardFooter
2822 * @extends Roo.bootstrap.Element
2823 * @parent Roo.bootstrap.Card
2824 * @children Roo.bootstrap.Component
2825 * Bootstrap CardFooter class
2828 * Create a new Card Footer - that you can embed children into
2829 * @param {Object} config The config object
2832 Roo.bootstrap.CardFooter = function(config){
2833 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2836 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2839 container_method : 'getCardFooter'
2852 * Card header - holder for the card header elements.
2857 * @class Roo.bootstrap.CardImageTop
2858 * @extends Roo.bootstrap.Element
2859 * @parent Roo.bootstrap.Card
2860 * @children Roo.bootstrap.Component
2861 * Bootstrap CardImageTop class
2864 * Create a new Card Image Top container
2865 * @param {Object} config The config object
2868 Roo.bootstrap.CardImageTop = function(config){
2869 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2872 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2875 container_method : 'getCardImageTop'
2890 * @class Roo.bootstrap.ButtonUploader
2891 * @extends Roo.bootstrap.Button
2892 * Bootstrap Button Uploader class - it's a button which when you add files to it
2895 * @cfg {Number} errorTimeout default 3000
2896 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2897 * @cfg {Array} html The button text.
2898 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2901 * Create a new CardUploader
2902 * @param {Object} config The config object
2905 Roo.bootstrap.ButtonUploader = function(config){
2909 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2915 * @event beforeselect
2916 * When button is pressed, before show upload files dialog is shown
2917 * @param {Roo.bootstrap.UploaderButton} this
2920 'beforeselect' : true,
2922 * @event fired when files have been selected,
2923 * When a the download link is clicked
2924 * @param {Roo.bootstrap.UploaderButton} this
2925 * @param {Array} Array of files that have been uploaded
2932 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2935 errorTimeout : 3000,
2939 fileCollection : false,
2944 getAutoCreate : function()
2951 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2959 initEvents : function()
2962 Roo.bootstrap.Button.prototype.initEvents.call(this);
2968 this.urlAPI = (window.createObjectURL && window) ||
2969 (window.URL && URL.revokeObjectURL && URL) ||
2970 (window.webkitURL && webkitURL);
2975 cls : 'd-none roo-card-upload-selector'
2978 if (this.multiple) {
2979 im.multiple = 'multiple';
2981 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2983 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2985 this.selectorEl.on('change', this.onFileSelected, this);
2992 onClick : function(e)
2996 if ( this.fireEvent('beforeselect', this) === false) {
3000 this.selectorEl.dom.click();
3004 onFileSelected : function(e)
3008 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3011 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3012 this.selectorEl.dom.value = '';// hopefully reset..
3014 this.fireEvent('uploaded', this, files );
3022 * addCard - add an Attachment to the uploader
3023 * @param data - the data about the image to upload
3027 title : "Title of file",
3028 is_uploaded : false,
3029 src : "http://.....",
3030 srcfile : { the File upload object },
3031 mimetype : file.type,
3034 .. any other data...
3059 * @class Roo.bootstrap.Img
3060 * @extends Roo.bootstrap.Component
3061 * Bootstrap Img class
3062 * @cfg {Boolean} imgResponsive false | true
3063 * @cfg {String} border rounded | circle | thumbnail
3064 * @cfg {String} src image source
3065 * @cfg {String} alt image alternative text
3066 * @cfg {String} href a tag href
3067 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3068 * @cfg {String} xsUrl xs image source
3069 * @cfg {String} smUrl sm image source
3070 * @cfg {String} mdUrl md image source
3071 * @cfg {String} lgUrl lg image source
3072 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3075 * Create a new Input
3076 * @param {Object} config The config object
3079 Roo.bootstrap.Img = function(config){
3080 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3086 * The img click event for the img.
3087 * @param {Roo.EventObject} e
3092 * The when any image loads
3093 * @param {Roo.EventObject} e
3099 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3101 imgResponsive: true,
3110 backgroundContain : false,
3112 getAutoCreate : function()
3114 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3115 return this.createSingleImg();
3120 cls: 'roo-image-responsive-group',
3125 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3127 if(!_this[size + 'Url']){
3133 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3134 html: _this.html || cfg.html,
3135 src: _this[size + 'Url']
3138 img.cls += ' roo-image-responsive-' + size;
3140 var s = ['xs', 'sm', 'md', 'lg'];
3142 s.splice(s.indexOf(size), 1);
3144 Roo.each(s, function(ss){
3145 img.cls += ' hidden-' + ss;
3148 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3149 cfg.cls += ' img-' + _this.border;
3153 cfg.alt = _this.alt;
3166 a.target = _this.target;
3170 cfg.cn.push((_this.href) ? a : img);
3177 createSingleImg : function()
3181 cls: (this.imgResponsive) ? 'img-responsive' : '',
3183 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3186 if (this.backgroundContain) {
3187 cfg.cls += ' background-contain';
3190 cfg.html = this.html || cfg.html;
3192 if (this.backgroundContain) {
3193 cfg.style="background-image: url(" + this.src + ')';
3195 cfg.src = this.src || cfg.src;
3198 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3199 cfg.cls += ' img-' + this.border;
3216 a.target = this.target;
3221 return (this.href) ? a : cfg;
3224 initEvents: function()
3227 this.el.on('click', this.onClick, this);
3229 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3230 this.el.on('load', this.onImageLoad, this);
3232 // not sure if this works.. not tested
3233 this.el.select('img', true).on('load', this.onImageLoad, this);
3238 onClick : function(e)
3240 Roo.log('img onclick');
3241 this.fireEvent('click', this, e);
3243 onImageLoad: function(e)
3245 Roo.log('img load');
3246 this.fireEvent('load', this, e);
3250 * Sets the url of the image - used to update it
3251 * @param {String} url the url of the image
3254 setSrc : function(url)
3258 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3259 if (this.backgroundContain) {
3260 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3262 this.el.dom.src = url;
3267 this.el.select('img', true).first().dom.src = url;
3283 * @class Roo.bootstrap.Link
3284 * @extends Roo.bootstrap.Component
3285 * @children Roo.bootstrap.Component
3286 * Bootstrap Link Class (eg. '<a href>')
3288 * @cfg {String} alt image alternative text
3289 * @cfg {String} href a tag href
3290 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3291 * @cfg {String} html the content of the link.
3292 * @cfg {String} anchor name for the anchor link
3293 * @cfg {String} fa - favicon
3295 * @cfg {Boolean} preventDefault (true | false) default false
3299 * Create a new Input
3300 * @param {Object} config The config object
3303 Roo.bootstrap.Link = function(config){
3304 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3310 * The img click event for the img.
3311 * @param {Roo.EventObject} e
3317 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3321 preventDefault: false,
3327 getAutoCreate : function()
3329 var html = this.html || '';
3331 if (this.fa !== false) {
3332 html = '<i class="fa fa-' + this.fa + '"></i>';
3337 // anchor's do not require html/href...
3338 if (this.anchor === false) {
3340 cfg.href = this.href || '#';
3342 cfg.name = this.anchor;
3343 if (this.html !== false || this.fa !== false) {
3346 if (this.href !== false) {
3347 cfg.href = this.href;
3351 if(this.alt !== false){
3356 if(this.target !== false) {
3357 cfg.target = this.target;
3363 initEvents: function() {
3365 if(!this.href || this.preventDefault){
3366 this.el.on('click', this.onClick, this);
3370 onClick : function(e)
3372 if(this.preventDefault){
3375 //Roo.log('img onclick');
3376 this.fireEvent('click', this, e);
3389 * @class Roo.bootstrap.Header
3390 * @extends Roo.bootstrap.Component
3391 * @children Roo.bootstrap.Component
3392 * Bootstrap Header class
3395 * @cfg {String} html content of header
3396 * @cfg {Number} level (1|2|3|4|5|6) default 1
3399 * Create a new Header
3400 * @param {Object} config The config object
3404 Roo.bootstrap.Header = function(config){
3405 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3408 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3416 getAutoCreate : function(){
3421 tag: 'h' + (1 *this.level),
3422 html: this.html || ''
3433 * @class Roo.bootstrap.MenuMgr
3435 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3438 Roo.bootstrap.menu.Manager = function(){
3439 var menus, active, groups = {}, attached = false, lastShow = new Date();
3441 // private - called when first menu is created
3444 active = new Roo.util.MixedCollection();
3445 Roo.get(document).addKeyListener(27, function(){
3446 if(active.length > 0){
3454 if(active && active.length > 0){
3455 var c = active.clone();
3465 if(active.length < 1){
3466 Roo.get(document).un("mouseup", onMouseDown);
3474 var last = active.last();
3475 lastShow = new Date();
3478 Roo.get(document).on("mouseup", onMouseDown);
3483 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3484 m.parentMenu.activeChild = m;
3485 }else if(last && last.isVisible()){
3486 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3491 function onBeforeHide(m){
3493 m.activeChild.hide();
3495 if(m.autoHideTimer){
3496 clearTimeout(m.autoHideTimer);
3497 delete m.autoHideTimer;
3502 function onBeforeShow(m){
3503 var pm = m.parentMenu;
3504 if(!pm && !m.allowOtherMenus){
3506 }else if(pm && pm.activeChild && active != m){
3507 pm.activeChild.hide();
3511 // private this should really trigger on mouseup..
3512 function onMouseDown(e){
3513 Roo.log("on Mouse Up");
3515 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3516 Roo.log("MenuManager hideAll");
3525 function onBeforeCheck(mi, state){
3527 var g = groups[mi.group];
3528 for(var i = 0, l = g.length; i < l; i++){
3530 g[i].setChecked(false);
3539 * Hides all menus that are currently visible
3541 hideAll : function(){
3546 register : function(menu){
3550 menus[menu.id] = menu;
3551 menu.on("beforehide", onBeforeHide);
3552 menu.on("hide", onHide);
3553 menu.on("beforeshow", onBeforeShow);
3554 menu.on("show", onShow);
3556 if(g && menu.events["checkchange"]){
3560 groups[g].push(menu);
3561 menu.on("checkchange", onCheck);
3566 * Returns a {@link Roo.menu.Menu} object
3567 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3568 * be used to generate and return a new Menu instance.
3570 get : function(menu){
3571 if(typeof menu == "string"){ // menu id
3573 }else if(menu.events){ // menu instance
3576 /*else if(typeof menu.length == 'number'){ // array of menu items?
3577 return new Roo.bootstrap.Menu({items:menu});
3578 }else{ // otherwise, must be a config
3579 return new Roo.bootstrap.Menu(menu);
3586 unregister : function(menu){
3587 delete menus[menu.id];
3588 menu.un("beforehide", onBeforeHide);
3589 menu.un("hide", onHide);
3590 menu.un("beforeshow", onBeforeShow);
3591 menu.un("show", onShow);
3593 if(g && menu.events["checkchange"]){
3594 groups[g].remove(menu);
3595 menu.un("checkchange", onCheck);
3600 registerCheckable : function(menuItem){
3601 var g = menuItem.group;
3606 groups[g].push(menuItem);
3607 menuItem.on("beforecheckchange", onBeforeCheck);
3612 unregisterCheckable : function(menuItem){
3613 var g = menuItem.group;
3615 groups[g].remove(menuItem);
3616 menuItem.un("beforecheckchange", onBeforeCheck);
3622 * @class Roo.bootstrap.menu.Menu
3623 * @extends Roo.bootstrap.Component
3625 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3627 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3629 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3630 * @cfg {bool} hidden if the menu should be hidden when rendered.
3631 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3632 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3633 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3634 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3638 * @param {Object} config The config objectQ
3642 Roo.bootstrap.menu.Menu = function(config){
3644 if (config.type == 'treeview') {
3645 // normally menu's are drawn attached to the document to handle layering etc..
3646 // however treeview (used by the docs menu is drawn into the parent element)
3647 this.container_method = 'getChildContainer';
3650 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3651 if (this.registerMenu && this.type != 'treeview') {
3652 Roo.bootstrap.menu.Manager.register(this);
3659 * Fires before this menu is displayed (return false to block)
3660 * @param {Roo.menu.Menu} this
3665 * Fires before this menu is hidden (return false to block)
3666 * @param {Roo.menu.Menu} this
3671 * Fires after this menu is displayed
3672 * @param {Roo.menu.Menu} this
3677 * Fires after this menu is hidden
3678 * @param {Roo.menu.Menu} this
3683 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3684 * @param {Roo.menu.Menu} this
3685 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3686 * @param {Roo.EventObject} e
3691 * Fires when the mouse is hovering over this menu
3692 * @param {Roo.menu.Menu} this
3693 * @param {Roo.EventObject} e
3694 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3699 * Fires when the mouse exits this menu
3700 * @param {Roo.menu.Menu} this
3701 * @param {Roo.EventObject} e
3702 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3707 * Fires when a menu item contained in this menu is clicked
3708 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3709 * @param {Roo.EventObject} e
3713 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3716 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3720 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3723 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3725 registerMenu : true,
3727 menuItems :false, // stores the menu items..
3737 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3739 hideTrigger : false,
3744 getChildContainer : function() {
3748 getAutoCreate : function(){
3750 //if (['right'].indexOf(this.align)!==-1) {
3751 // cfg.cn[1].cls += ' pull-right'
3756 cls : 'dropdown-menu shadow' ,
3757 style : 'z-index:1000'
3761 if (this.type === 'submenu') {
3762 cfg.cls = 'submenu active';
3764 if (this.type === 'treeview') {
3765 cfg.cls = 'treeview-menu';
3770 initEvents : function() {
3772 // Roo.log("ADD event");
3773 // Roo.log(this.triggerEl.dom);
3774 if (this.triggerEl) {
3776 this.triggerEl.on('click', this.onTriggerClick, this);
3778 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3780 if (!this.hideTrigger) {
3781 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3782 // dropdown toggle on the 'a' in BS4?
3783 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3785 this.triggerEl.addClass('dropdown-toggle');
3791 this.el.on('touchstart' , this.onTouch, this);
3793 this.el.on('click' , this.onClick, this);
3795 this.el.on("mouseover", this.onMouseOver, this);
3796 this.el.on("mouseout", this.onMouseOut, this);
3800 findTargetItem : function(e)
3802 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3806 //Roo.log(t); Roo.log(t.id);
3808 //Roo.log(this.menuitems);
3809 return this.menuitems.get(t.id);
3811 //return this.items.get(t.menuItemId);
3817 onTouch : function(e)
3819 Roo.log("menu.onTouch");
3820 //e.stopEvent(); this make the user popdown broken
3824 onClick : function(e)
3826 Roo.log("menu.onClick");
3828 var t = this.findTargetItem(e);
3829 if(!t || t.isContainer){
3834 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3835 if(t == this.activeItem && t.shouldDeactivate(e)){
3836 this.activeItem.deactivate();
3837 delete this.activeItem;
3841 this.setActiveItem(t, true);
3849 Roo.log('pass click event');
3853 this.fireEvent("click", this, t, e);
3857 if(!t.href.length || t.href == '#'){
3858 (function() { _this.hide(); }).defer(100);
3863 onMouseOver : function(e){
3864 var t = this.findTargetItem(e);
3867 // if(t.canActivate && !t.disabled){
3868 // this.setActiveItem(t, true);
3872 this.fireEvent("mouseover", this, e, t);
3874 isVisible : function(){
3875 return !this.hidden;
3877 onMouseOut : function(e){
3878 var t = this.findTargetItem(e);
3881 // if(t == this.activeItem && t.shouldDeactivate(e)){
3882 // this.activeItem.deactivate();
3883 // delete this.activeItem;
3886 this.fireEvent("mouseout", this, e, t);
3891 * Displays this menu relative to another element
3892 * @param {String/HTMLElement/Roo.Element} element The element to align to
3893 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3894 * the element (defaults to this.defaultAlign)
3895 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3897 show : function(el, pos, parentMenu)
3899 if (false === this.fireEvent("beforeshow", this)) {
3900 Roo.log("show canceled");
3903 this.parentMenu = parentMenu;
3907 this.el.addClass('show'); // show otherwise we do not know how big we are..
3909 var xy = this.el.getAlignToXY(el, pos);
3911 // bl-tl << left align below
3912 // tl-bl << left align
3914 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3915 // if it goes to far to the right.. -> align left.
3916 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3919 // was left align - go right?
3920 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3923 // goes down the bottom
3924 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3926 var a = this.align.replace('?', '').split('-');
3927 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3931 this.showAt( xy , parentMenu, false);
3934 * Displays this menu at a specific xy position
3935 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3936 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3938 showAt : function(xy, parentMenu, /* private: */_e){
3939 this.parentMenu = parentMenu;
3944 this.fireEvent("beforeshow", this);
3945 //xy = this.el.adjustForConstraints(xy);
3949 this.hideMenuItems();
3950 this.hidden = false;
3951 if (this.triggerEl) {
3952 this.triggerEl.addClass('open');
3955 this.el.addClass('show');
3959 // reassign x when hitting right
3961 // reassign y when hitting bottom
3963 // but the list may align on trigger left or trigger top... should it be a properity?
3965 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3970 this.fireEvent("show", this);
3976 this.doFocus.defer(50, this);
3980 doFocus : function(){
3982 this.focusEl.focus();
3987 * Hides this menu and optionally all parent menus
3988 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3990 hide : function(deep)
3992 if (false === this.fireEvent("beforehide", this)) {
3993 Roo.log("hide canceled");
3996 this.hideMenuItems();
3997 if(this.el && this.isVisible()){
3999 if(this.activeItem){
4000 this.activeItem.deactivate();
4001 this.activeItem = null;
4003 if (this.triggerEl) {
4004 this.triggerEl.removeClass('open');
4007 this.el.removeClass('show');
4009 this.fireEvent("hide", this);
4011 if(deep === true && this.parentMenu){
4012 this.parentMenu.hide(true);
4016 onTriggerClick : function(e)
4018 Roo.log('trigger click');
4020 var target = e.getTarget();
4022 Roo.log(target.nodeName.toLowerCase());
4024 if(target.nodeName.toLowerCase() === 'i'){
4030 onTriggerPress : function(e)
4032 Roo.log('trigger press');
4033 //Roo.log(e.getTarget());
4034 // Roo.log(this.triggerEl.dom);
4036 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4037 var pel = Roo.get(e.getTarget());
4038 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4039 Roo.log('is treeview or dropdown?');
4043 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4047 if (this.isVisible()) {
4053 this.show(this.triggerEl, this.align, false);
4056 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4063 hideMenuItems : function()
4065 Roo.log("hide Menu Items");
4070 this.el.select('.open',true).each(function(aa) {
4072 aa.removeClass('open');
4076 addxtypeChild : function (tree, cntr) {
4077 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4079 this.menuitems.add(comp);
4091 this.getEl().dom.innerHTML = '';
4092 this.menuitems.clear();
4098 * @class Roo.bootstrap.menu.Item
4099 * @extends Roo.bootstrap.Component
4100 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4101 * @parent Roo.bootstrap.menu.Menu
4103 * Bootstrap MenuItem class
4105 * @cfg {String} html the menu label
4106 * @cfg {String} href the link
4107 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4108 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4109 * @cfg {Boolean} active used on sidebars to highlight active itesm
4110 * @cfg {String} fa favicon to show on left of menu item.
4111 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4115 * Create a new MenuItem
4116 * @param {Object} config The config object
4120 Roo.bootstrap.menu.Item = function(config){
4121 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4126 * The raw click event for the entire grid.
4127 * @param {Roo.bootstrap.menu.Item} this
4128 * @param {Roo.EventObject} e
4134 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4138 preventDefault: false,
4139 isContainer : false,
4143 getAutoCreate : function(){
4145 if(this.isContainer){
4148 cls: 'dropdown-menu-item '
4158 cls : 'dropdown-item',
4163 if (this.fa !== false) {
4166 cls : 'fa fa-' + this.fa
4175 cls: 'dropdown-menu-item',
4178 if (this.parent().type == 'treeview') {
4179 cfg.cls = 'treeview-menu';
4182 cfg.cls += ' active';
4187 anc.href = this.href || cfg.cn[0].href ;
4188 ctag.html = this.html || cfg.cn[0].html ;
4192 initEvents: function()
4194 if (this.parent().type == 'treeview') {
4195 this.el.select('a').on('click', this.onClick, this);
4199 this.menu.parentType = this.xtype;
4200 this.menu.triggerEl = this.el;
4201 this.menu = this.addxtype(Roo.apply({}, this.menu));
4205 onClick : function(e)
4207 //Roo.log('item on click ');
4209 if(this.href === false || this.preventDefault){
4212 //this.parent().hideMenuItems();
4214 this.fireEvent('click', this, e);
4228 * @class Roo.bootstrap.menu.Separator
4229 * @extends Roo.bootstrap.Component
4231 * @parent Roo.bootstrap.menu.Menu
4232 * Bootstrap Separator class
4235 * Create a new Separator
4236 * @param {Object} config The config object
4240 Roo.bootstrap.menu.Separator = function(config){
4241 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4244 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4246 getAutoCreate : function(){
4249 cls: 'dropdown-divider divider'
4265 * @class Roo.bootstrap.Modal
4266 * @extends Roo.bootstrap.Component
4267 * @parent none builder
4268 * @children Roo.bootstrap.Component
4269 * Bootstrap Modal class
4270 * @cfg {String} title Title of dialog
4271 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4272 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4273 * @cfg {Boolean} specificTitle default false
4274 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4275 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4276 * @cfg {Boolean} animate default true
4277 * @cfg {Boolean} allow_close default true
4278 * @cfg {Boolean} fitwindow default false
4279 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4280 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4281 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4282 * @cfg {String} size (sm|lg|xl) default empty
4283 * @cfg {Number} max_width set the max width of modal
4284 * @cfg {Boolean} editableTitle can the title be edited
4289 * Create a new Modal Dialog
4290 * @param {Object} config The config object
4293 Roo.bootstrap.Modal = function(config){
4294 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4299 * The raw btnclick event for the button
4300 * @param {Roo.EventObject} e
4305 * Fire when dialog resize
4306 * @param {Roo.bootstrap.Modal} this
4307 * @param {Roo.EventObject} e
4311 * @event titlechanged
4312 * Fire when the editable title has been changed
4313 * @param {Roo.bootstrap.Modal} this
4314 * @param {Roo.EventObject} value
4316 "titlechanged" : true
4319 this.buttons = this.buttons || [];
4322 this.tmpl = Roo.factory(this.tmpl);
4327 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4329 title : 'test dialog',
4339 specificTitle: false,
4341 buttonPosition: 'right',
4363 editableTitle : false,
4365 onRender : function(ct, position)
4367 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4370 var cfg = Roo.apply({}, this.getAutoCreate());
4373 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4375 //if (!cfg.name.length) {
4379 cfg.cls += ' ' + this.cls;
4382 cfg.style = this.style;
4384 this.el = Roo.get(document.body).createChild(cfg, position);
4386 //var type = this.el.dom.type;
4389 if(this.tabIndex !== undefined){
4390 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4393 this.dialogEl = this.el.select('.modal-dialog',true).first();
4394 this.bodyEl = this.el.select('.modal-body',true).first();
4395 this.closeEl = this.el.select('.modal-header .close', true).first();
4396 this.headerEl = this.el.select('.modal-header',true).first();
4397 this.titleEl = this.el.select('.modal-title',true).first();
4398 this.footerEl = this.el.select('.modal-footer',true).first();
4400 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4402 //this.el.addClass("x-dlg-modal");
4404 if (this.buttons.length) {
4405 Roo.each(this.buttons, function(bb) {
4406 var b = Roo.apply({}, bb);
4407 b.xns = b.xns || Roo.bootstrap;
4408 b.xtype = b.xtype || 'Button';
4409 if (typeof(b.listeners) == 'undefined') {
4410 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4413 var btn = Roo.factory(b);
4415 btn.render(this.getButtonContainer());
4419 // render the children.
4422 if(typeof(this.items) != 'undefined'){
4423 var items = this.items;
4426 for(var i =0;i < items.length;i++) {
4427 // we force children not to montor widnow resize - as we do that for them.
4428 items[i].monitorWindowResize = false;
4429 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4433 this.items = nitems;
4435 // where are these used - they used to be body/close/footer
4439 //this.el.addClass([this.fieldClass, this.cls]);
4443 getAutoCreate : function()
4445 // we will default to modal-body-overflow - might need to remove or make optional later.
4447 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4448 html : this.html || ''
4453 cls : 'modal-title',
4457 if(this.specificTitle){ // WTF is this?
4462 if (this.allow_close && Roo.bootstrap.version == 3) {
4472 if (this.editableTitle) {
4474 cls: 'form-control roo-editable-title d-none',
4480 if (this.allow_close && Roo.bootstrap.version == 4) {
4490 if(this.size.length){
4491 size = 'modal-' + this.size;
4494 var footer = Roo.bootstrap.version == 3 ?
4496 cls : 'modal-footer',
4500 cls: 'btn-' + this.buttonPosition
4505 { // BS4 uses mr-auto on left buttons....
4506 cls : 'modal-footer'
4517 cls: "modal-dialog " + size,
4520 cls : "modal-content",
4523 cls : 'modal-header',
4538 modal.cls += ' fade';
4544 getChildContainer : function() {
4549 getButtonContainer : function() {
4551 return Roo.bootstrap.version == 4 ?
4552 this.el.select('.modal-footer',true).first()
4553 : this.el.select('.modal-footer div',true).first();
4557 closeClick : function()
4562 initEvents : function()
4564 if (this.allow_close) {
4565 this.closeEl.on('click', this.closeClick, this);
4567 Roo.EventManager.onWindowResize(this.resize, this, true);
4568 if (this.editableTitle) {
4569 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4570 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4571 this.headerEditEl.on('keyup', function(e) {
4572 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4573 this.toggleHeaderInput(false)
4576 this.headerEditEl.on('blur', function(e) {
4577 this.toggleHeaderInput(false)
4586 this.maskEl.setSize(
4587 Roo.lib.Dom.getViewWidth(true),
4588 Roo.lib.Dom.getViewHeight(true)
4591 if (this.fitwindow) {
4593 this.dialogEl.setStyle( { 'max-width' : '100%' });
4595 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4596 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4601 if(this.max_width !== 0) {
4603 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4606 this.setSize(w, this.height);
4610 if(this.max_height) {
4611 this.setSize(w,Math.min(
4613 Roo.lib.Dom.getViewportHeight(true) - 60
4619 if(!this.fit_content) {
4620 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4624 this.setSize(w, Math.min(
4626 this.headerEl.getHeight() +
4627 this.footerEl.getHeight() +
4628 this.getChildHeight(this.bodyEl.dom.childNodes),
4629 Roo.lib.Dom.getViewportHeight(true) - 60)
4635 setSize : function(w,h)
4642 // any layout/border etc.. resize..
4644 this.items.forEach( function(e) {
4645 e.layout ? e.layout() : false;
4654 if (!this.rendered) {
4657 this.toggleHeaderInput(false);
4658 //this.el.setStyle('display', 'block');
4659 this.el.removeClass('hideing');
4660 this.el.dom.style.display='block';
4662 Roo.get(document.body).addClass('modal-open');
4664 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4667 this.el.addClass('show');
4668 this.el.addClass('in');
4671 this.el.addClass('show');
4672 this.el.addClass('in');
4675 // not sure how we can show data in here..
4677 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4680 Roo.get(document.body).addClass("x-body-masked");
4682 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4683 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4684 this.maskEl.dom.style.display = 'block';
4685 this.maskEl.addClass('show');
4690 this.fireEvent('show', this);
4692 // set zindex here - otherwise it appears to be ignored...
4693 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4696 // this is for children that are... layout.Border
4698 this.items.forEach( function(e) {
4699 e.layout ? e.layout() : false;
4707 if(this.fireEvent("beforehide", this) !== false){
4709 this.maskEl.removeClass('show');
4711 this.maskEl.dom.style.display = '';
4712 Roo.get(document.body).removeClass("x-body-masked");
4713 this.el.removeClass('in');
4714 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4716 if(this.animate){ // why
4717 this.el.addClass('hideing');
4718 this.el.removeClass('show');
4720 if (!this.el.hasClass('hideing')) {
4721 return; // it's been shown again...
4724 this.el.dom.style.display='';
4726 Roo.get(document.body).removeClass('modal-open');
4727 this.el.removeClass('hideing');
4731 this.el.removeClass('show');
4732 this.el.dom.style.display='';
4733 Roo.get(document.body).removeClass('modal-open');
4736 this.fireEvent('hide', this);
4739 isVisible : function()
4742 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4746 addButton : function(str, cb)
4750 var b = Roo.apply({}, { html : str } );
4751 b.xns = b.xns || Roo.bootstrap;
4752 b.xtype = b.xtype || 'Button';
4753 if (typeof(b.listeners) == 'undefined') {
4754 b.listeners = { click : cb.createDelegate(this) };
4757 var btn = Roo.factory(b);
4759 btn.render(this.getButtonContainer());
4765 setDefaultButton : function(btn)
4767 //this.el.select('.modal-footer').()
4770 resizeTo: function(w,h)
4772 this.dialogEl.setWidth(w);
4774 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4776 this.bodyEl.setHeight(h - diff);
4778 this.fireEvent('resize', this);
4781 setContentSize : function(w, h)
4785 onButtonClick: function(btn,e)
4788 this.fireEvent('btnclick', btn.name, e);
4791 * Set the title of the Dialog
4792 * @param {String} str new Title
4794 setTitle: function(str) {
4795 this.titleEl.dom.innerHTML = str;
4799 * Set the body of the Dialog
4800 * @param {String} str new Title
4802 setBody: function(str) {
4803 this.bodyEl.dom.innerHTML = str;
4806 * Set the body of the Dialog using the template
4807 * @param {Obj} data - apply this data to the template and replace the body contents.
4809 applyBody: function(obj)
4812 Roo.log("Error - using apply Body without a template");
4815 this.tmpl.overwrite(this.bodyEl, obj);
4818 getChildHeight : function(child_nodes)
4822 child_nodes.length == 0
4827 var child_height = 0;
4829 for(var i = 0; i < child_nodes.length; i++) {
4832 * for modal with tabs...
4833 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4835 var layout_childs = child_nodes[i].childNodes;
4837 for(var j = 0; j < layout_childs.length; j++) {
4839 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4841 var layout_body_childs = layout_childs[j].childNodes;
4843 for(var k = 0; k < layout_body_childs.length; k++) {
4845 if(layout_body_childs[k].classList.contains('navbar')) {
4846 child_height += layout_body_childs[k].offsetHeight;
4850 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4852 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4854 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4856 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4857 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4872 child_height += child_nodes[i].offsetHeight;
4873 // Roo.log(child_nodes[i].offsetHeight);
4876 return child_height;
4878 toggleHeaderInput : function(is_edit)
4880 if (!this.editableTitle) {
4881 return; // not editable.
4883 if (is_edit && this.is_header_editing) {
4884 return; // already editing..
4888 this.headerEditEl.dom.value = this.title;
4889 this.headerEditEl.removeClass('d-none');
4890 this.headerEditEl.dom.focus();
4891 this.titleEl.addClass('d-none');
4893 this.is_header_editing = true;
4896 // flip back to not editing.
4897 this.title = this.headerEditEl.dom.value;
4898 this.headerEditEl.addClass('d-none');
4899 this.titleEl.removeClass('d-none');
4900 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4901 this.is_header_editing = false;
4902 this.fireEvent('titlechanged', this, this.title);
4911 Roo.apply(Roo.bootstrap.Modal, {
4913 * Button config that displays a single OK button
4922 * Button config that displays Yes and No buttons
4938 * Button config that displays OK and Cancel buttons
4953 * Button config that displays Yes, No and Cancel buttons
4978 * messagebox - can be used as a replace
4982 * @class Roo.MessageBox
4983 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4987 Roo.Msg.alert('Status', 'Changes saved successfully.');
4989 // Prompt for user data:
4990 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4992 // process text value...
4996 // Show a dialog using config options:
4998 title:'Save Changes?',
4999 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
5000 buttons: Roo.Msg.YESNOCANCEL,
5007 Roo.bootstrap.MessageBox = function(){
5008 var dlg, opt, mask, waitTimer;
5009 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5010 var buttons, activeTextEl, bwidth;
5014 var handleButton = function(button){
5016 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5020 var handleHide = function(){
5022 dlg.el.removeClass(opt.cls);
5025 // Roo.TaskMgr.stop(waitTimer);
5026 // waitTimer = null;
5031 var updateButtons = function(b){
5034 buttons["ok"].hide();
5035 buttons["cancel"].hide();
5036 buttons["yes"].hide();
5037 buttons["no"].hide();
5038 dlg.footerEl.hide();
5042 dlg.footerEl.show();
5043 for(var k in buttons){
5044 if(typeof buttons[k] != "function"){
5047 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5048 width += buttons[k].el.getWidth()+15;
5058 var handleEsc = function(d, k, e){
5059 if(opt && opt.closable !== false){
5069 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5070 * @return {Roo.BasicDialog} The BasicDialog element
5072 getDialog : function(){
5074 dlg = new Roo.bootstrap.Modal( {
5077 //constraintoviewport:false,
5079 //collapsible : false,
5084 //buttonAlign:"center",
5085 closeClick : function(){
5086 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5089 handleButton("cancel");
5094 dlg.on("hide", handleHide);
5096 //dlg.addKeyListener(27, handleEsc);
5098 this.buttons = buttons;
5099 var bt = this.buttonText;
5100 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5101 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5102 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5103 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5105 bodyEl = dlg.bodyEl.createChild({
5107 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5108 '<textarea class="roo-mb-textarea"></textarea>' +
5109 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5111 msgEl = bodyEl.dom.firstChild;
5112 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5113 textboxEl.enableDisplayMode();
5114 textboxEl.addKeyListener([10,13], function(){
5115 if(dlg.isVisible() && opt && opt.buttons){
5118 }else if(opt.buttons.yes){
5119 handleButton("yes");
5123 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5124 textareaEl.enableDisplayMode();
5125 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5126 progressEl.enableDisplayMode();
5128 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5129 var pf = progressEl.dom.firstChild;
5131 pp = Roo.get(pf.firstChild);
5132 pp.setHeight(pf.offsetHeight);
5140 * Updates the message box body text
5141 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5142 * the XHTML-compliant non-breaking space character '&#160;')
5143 * @return {Roo.MessageBox} This message box
5145 updateText : function(text)
5147 if(!dlg.isVisible() && !opt.width){
5148 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5149 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5151 msgEl.innerHTML = text || ' ';
5153 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5154 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5156 Math.min(opt.width || cw , this.maxWidth),
5157 Math.max(opt.minWidth || this.minWidth, bwidth)
5160 activeTextEl.setWidth(w);
5162 if(dlg.isVisible()){
5163 dlg.fixedcenter = false;
5165 // to big, make it scroll. = But as usual stupid IE does not support
5168 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5169 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5170 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5172 bodyEl.dom.style.height = '';
5173 bodyEl.dom.style.overflowY = '';
5176 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5178 bodyEl.dom.style.overflowX = '';
5181 dlg.setContentSize(w, bodyEl.getHeight());
5182 if(dlg.isVisible()){
5183 dlg.fixedcenter = true;
5189 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5190 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5191 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5192 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5193 * @return {Roo.MessageBox} This message box
5195 updateProgress : function(value, text){
5197 this.updateText(text);
5200 if (pp) { // weird bug on my firefox - for some reason this is not defined
5201 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5202 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5208 * Returns true if the message box is currently displayed
5209 * @return {Boolean} True if the message box is visible, else false
5211 isVisible : function(){
5212 return dlg && dlg.isVisible();
5216 * Hides the message box if it is displayed
5219 if(this.isVisible()){
5225 * Displays a new message box, or reinitializes an existing message box, based on the config options
5226 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5227 * The following config object properties are supported:
5229 Property Type Description
5230 ---------- --------------- ------------------------------------------------------------------------------------
5231 animEl String/Element An id or Element from which the message box should animate as it opens and
5232 closes (defaults to undefined)
5233 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5234 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5235 closable Boolean False to hide the top-right close button (defaults to true). Note that
5236 progress and wait dialogs will ignore this property and always hide the
5237 close button as they can only be closed programmatically.
5238 cls String A custom CSS class to apply to the message box element
5239 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5240 displayed (defaults to 75)
5241 fn Function A callback function to execute after closing the dialog. The arguments to the
5242 function will be btn (the name of the button that was clicked, if applicable,
5243 e.g. "ok"), and text (the value of the active text field, if applicable).
5244 Progress and wait dialogs will ignore this option since they do not respond to
5245 user actions and can only be closed programmatically, so any required function
5246 should be called by the same code after it closes the dialog.
5247 icon String A CSS class that provides a background image to be used as an icon for
5248 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5249 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5250 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5251 modal Boolean False to allow user interaction with the page while the message box is
5252 displayed (defaults to true)
5253 msg String A string that will replace the existing message box body text (defaults
5254 to the XHTML-compliant non-breaking space character ' ')
5255 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5256 progress Boolean True to display a progress bar (defaults to false)
5257 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5258 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5259 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5260 title String The title text
5261 value String The string value to set into the active textbox element if displayed
5262 wait Boolean True to display a progress bar (defaults to false)
5263 width Number The width of the dialog in pixels
5270 msg: 'Please enter your address:',
5272 buttons: Roo.MessageBox.OKCANCEL,
5275 animEl: 'addAddressBtn'
5278 * @param {Object} config Configuration options
5279 * @return {Roo.MessageBox} This message box
5281 show : function(options)
5284 // this causes nightmares if you show one dialog after another
5285 // especially on callbacks..
5287 if(this.isVisible()){
5290 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5291 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5292 Roo.log("New Dialog Message:" + options.msg )
5293 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5294 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5297 var d = this.getDialog();
5299 d.setTitle(opt.title || " ");
5300 d.closeEl.setDisplayed(opt.closable !== false);
5301 activeTextEl = textboxEl;
5302 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5307 textareaEl.setHeight(typeof opt.multiline == "number" ?
5308 opt.multiline : this.defaultTextHeight);
5309 activeTextEl = textareaEl;
5318 progressEl.setDisplayed(opt.progress === true);
5320 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5322 this.updateProgress(0);
5323 activeTextEl.dom.value = opt.value || "";
5325 dlg.setDefaultButton(activeTextEl);
5327 var bs = opt.buttons;
5331 }else if(bs && bs.yes){
5332 db = buttons["yes"];
5334 dlg.setDefaultButton(db);
5336 bwidth = updateButtons(opt.buttons);
5337 this.updateText(opt.msg);
5339 d.el.addClass(opt.cls);
5341 d.proxyDrag = opt.proxyDrag === true;
5342 d.modal = opt.modal !== false;
5343 d.mask = opt.modal !== false ? mask : false;
5345 // force it to the end of the z-index stack so it gets a cursor in FF
5346 document.body.appendChild(dlg.el.dom);
5347 d.animateTarget = null;
5348 d.show(options.animEl);
5354 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5355 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5356 * and closing the message box when the process is complete.
5357 * @param {String} title The title bar text
5358 * @param {String} msg The message box body text
5359 * @return {Roo.MessageBox} This message box
5361 progress : function(title, msg){
5368 minWidth: this.minProgressWidth,
5375 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5376 * If a callback function is passed it will be called after the user clicks the button, and the
5377 * id of the button that was clicked will be passed as the only parameter to the callback
5378 * (could also be the top-right close button).
5379 * @param {String} title The title bar text
5380 * @param {String} msg The message box body text
5381 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5382 * @param {Object} scope (optional) The scope of the callback function
5383 * @return {Roo.MessageBox} This message box
5385 alert : function(title, msg, fn, scope)
5400 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5401 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5402 * You are responsible for closing the message box when the process is complete.
5403 * @param {String} msg The message box body text
5404 * @param {String} title (optional) The title bar text
5405 * @return {Roo.MessageBox} This message box
5407 wait : function(msg, title){
5418 waitTimer = Roo.TaskMgr.start({
5420 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5428 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5429 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5430 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5431 * @param {String} title The title bar text
5432 * @param {String} msg The message box body text
5433 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434 * @param {Object} scope (optional) The scope of the callback function
5435 * @return {Roo.MessageBox} This message box
5437 confirm : function(title, msg, fn, scope){
5441 buttons: this.YESNO,
5450 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5451 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5452 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5453 * (could also be the top-right close button) and the text that was entered will be passed as the two
5454 * parameters to the callback.
5455 * @param {String} title The title bar text
5456 * @param {String} msg The message box body text
5457 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5458 * @param {Object} scope (optional) The scope of the callback function
5459 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5460 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5461 * @return {Roo.MessageBox} This message box
5463 prompt : function(title, msg, fn, scope, multiline){
5467 buttons: this.OKCANCEL,
5472 multiline: multiline,
5479 * Button config that displays a single OK button
5484 * Button config that displays Yes and No buttons
5487 YESNO : {yes:true, no:true},
5489 * Button config that displays OK and Cancel buttons
5492 OKCANCEL : {ok:true, cancel:true},
5494 * Button config that displays Yes, No and Cancel buttons
5497 YESNOCANCEL : {yes:true, no:true, cancel:true},
5500 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5503 defaultTextHeight : 75,
5505 * The maximum width in pixels of the message box (defaults to 600)
5510 * The minimum width in pixels of the message box (defaults to 100)
5515 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5516 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5519 minProgressWidth : 250,
5521 * An object containing the default button text strings that can be overriden for localized language support.
5522 * Supported properties are: ok, cancel, yes and no.
5523 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5536 * Shorthand for {@link Roo.MessageBox}
5538 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5539 Roo.Msg = Roo.Msg || Roo.MessageBox;
5548 * @class Roo.bootstrap.nav.Bar
5549 * @extends Roo.bootstrap.Component
5551 * Bootstrap Navbar class
5554 * Create a new Navbar
5555 * @param {Object} config The config object
5559 Roo.bootstrap.nav.Bar = function(config){
5560 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5564 * @event beforetoggle
5565 * Fire before toggle the menu
5566 * @param {Roo.EventObject} e
5568 "beforetoggle" : true
5572 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5581 getAutoCreate : function(){
5584 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5588 initEvents :function ()
5590 //Roo.log(this.el.select('.navbar-toggle',true));
5591 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5598 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5600 var size = this.el.getSize();
5601 this.maskEl.setSize(size.width, size.height);
5602 this.maskEl.enableDisplayMode("block");
5611 getChildContainer : function()
5613 if (this.el && this.el.select('.collapse').getCount()) {
5614 return this.el.select('.collapse',true).first();
5629 onToggle : function()
5632 if(this.fireEvent('beforetoggle', this) === false){
5635 var ce = this.el.select('.navbar-collapse',true).first();
5637 if (!ce.hasClass('show')) {
5647 * Expand the navbar pulldown
5649 expand : function ()
5652 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing')) {
5656 ce.dom.style.height = '';
5658 ce.addClass('in'); // old...
5659 ce.removeClass('collapse');
5660 ce.addClass('show');
5661 var h = ce.getHeight();
5663 ce.removeClass('show');
5664 // at this point we should be able to see it..
5665 ce.addClass('collapsing');
5667 ce.setHeight(0); // resize it ...
5668 ce.on('transitionend', function() {
5669 //Roo.log('done transition');
5670 ce.removeClass('collapsing');
5671 ce.addClass('show');
5672 ce.removeClass('collapse');
5674 ce.dom.style.height = '';
5675 }, this, { single: true} );
5677 ce.dom.scrollTop = 0;
5680 * Collapse the navbar pulldown
5682 collapse : function()
5684 var ce = this.el.select('.navbar-collapse',true).first();
5686 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5687 // it's collapsed or collapsing..
5690 ce.removeClass('in'); // old...
5691 ce.setHeight(ce.getHeight());
5692 ce.removeClass('show');
5693 ce.addClass('collapsing');
5695 ce.on('transitionend', function() {
5696 ce.dom.style.height = '';
5697 ce.removeClass('collapsing');
5698 ce.addClass('collapse');
5699 }, this, { single: true} );
5719 * @class Roo.bootstrap.nav.Simplebar
5720 * @extends Roo.bootstrap.nav.Bar
5721 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5722 * Bootstrap Sidebar class
5724 * @cfg {Boolean} inverse is inverted color
5726 * @cfg {String} type (nav | pills | tabs)
5727 * @cfg {Boolean} arrangement stacked | justified
5728 * @cfg {String} align (left | right) alignment
5730 * @cfg {Boolean} main (true|false) main nav bar? default false
5731 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5733 * @cfg {String} tag (header|footer|nav|div) default is nav
5735 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5739 * Create a new Sidebar
5740 * @param {Object} config The config object
5744 Roo.bootstrap.nav.Simplebar = function(config){
5745 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5748 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5764 getAutoCreate : function(){
5768 tag : this.tag || 'div',
5769 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5771 if (['light','white'].indexOf(this.weight) > -1) {
5772 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5774 cfg.cls += ' bg-' + this.weight;
5777 cfg.cls += ' navbar-inverse';
5781 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5783 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5792 cls: 'nav nav-' + this.xtype,
5798 this.type = this.type || 'nav';
5799 if (['tabs','pills'].indexOf(this.type) != -1) {
5800 cfg.cn[0].cls += ' nav-' + this.type
5804 if (this.type!=='nav') {
5805 Roo.log('nav type must be nav/tabs/pills')
5807 cfg.cn[0].cls += ' navbar-nav'
5813 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5814 cfg.cn[0].cls += ' nav-' + this.arrangement;
5818 if (this.align === 'right') {
5819 cfg.cn[0].cls += ' navbar-right';
5844 * navbar-expand-md fixed-top
5848 * @class Roo.bootstrap.nav.Headerbar
5849 * @extends Roo.bootstrap.nav.Simplebar
5850 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5851 * Bootstrap Sidebar class
5853 * @cfg {String} brand what is brand
5854 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5855 * @cfg {String} brand_href href of the brand
5856 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5857 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5858 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5859 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5862 * Create a new Sidebar
5863 * @param {Object} config The config object
5867 Roo.bootstrap.nav.Headerbar = function(config){
5868 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5872 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5879 desktopCenter : false,
5882 getAutoCreate : function(){
5885 tag: this.nav || 'nav',
5886 cls: 'navbar navbar-expand-md',
5892 if (this.desktopCenter) {
5893 cn.push({cls : 'container', cn : []});
5901 cls: 'navbar-toggle navbar-toggler',
5902 'data-toggle': 'collapse',
5907 html: 'Toggle navigation'
5911 cls: 'icon-bar navbar-toggler-icon'
5924 cn.push( Roo.bootstrap.version == 4 ? btn : {
5926 cls: 'navbar-header',
5935 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5939 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5941 if (['light','white'].indexOf(this.weight) > -1) {
5942 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5944 cfg.cls += ' bg-' + this.weight;
5947 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5948 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5950 // tag can override this..
5952 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5955 if (this.brand !== '') {
5956 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5957 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5959 href: this.brand_href ? this.brand_href : '#',
5960 cls: 'navbar-brand',
5968 cfg.cls += ' main-nav';
5976 getHeaderChildContainer : function()
5978 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5979 return this.el.select('.navbar-header',true).first();
5982 return this.getChildContainer();
5985 getChildContainer : function()
5988 return this.el.select('.roo-navbar-collapse',true).first();
5993 initEvents : function()
5995 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5997 if (this.autohide) {
6002 Roo.get(document).on('scroll',function(e) {
6003 var ns = Roo.get(document).getScroll().top;
6004 var os = prevScroll;
6008 ft.removeClass('slideDown');
6009 ft.addClass('slideUp');
6012 ft.removeClass('slideUp');
6013 ft.addClass('slideDown');
6034 * @class Roo.bootstrap.nav.Sidebar
6035 * @extends Roo.bootstrap.nav.Bar
6036 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6037 * Bootstrap Sidebar class
6040 * Create a new Sidebar
6041 * @param {Object} config The config object
6045 Roo.bootstrap.nav.Sidebar = function(config){
6046 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6049 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6051 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6053 getAutoCreate : function(){
6058 cls: 'sidebar sidebar-nav'
6080 * @class Roo.bootstrap.nav.Group
6081 * @extends Roo.bootstrap.Component
6082 * @children Roo.bootstrap.nav.Item
6083 * Bootstrap NavGroup class
6084 * @cfg {String} align (left|right)
6085 * @cfg {Boolean} inverse
6086 * @cfg {String} type (nav|pills|tab) default nav
6087 * @cfg {String} navId - reference Id for navbar.
6088 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6091 * Create a new nav group
6092 * @param {Object} config The config object
6095 Roo.bootstrap.nav.Group = function(config){
6096 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6099 Roo.bootstrap.nav.Group.register(this);
6103 * Fires when the active item changes
6104 * @param {Roo.bootstrap.nav.Group} this
6105 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6106 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6113 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6125 getAutoCreate : function()
6127 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6133 if (Roo.bootstrap.version == 4) {
6134 if (['tabs','pills'].indexOf(this.type) != -1) {
6135 cfg.cls += ' nav-' + this.type;
6137 // trying to remove so header bar can right align top?
6138 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6139 // do not use on header bar...
6140 cfg.cls += ' navbar-nav';
6145 if (['tabs','pills'].indexOf(this.type) != -1) {
6146 cfg.cls += ' nav-' + this.type
6148 if (this.type !== 'nav') {
6149 Roo.log('nav type must be nav/tabs/pills')
6151 cfg.cls += ' navbar-nav'
6155 if (this.parent() && this.parent().sidebar) {
6158 cls: 'dashboard-menu sidebar-menu'
6164 if (this.form === true) {
6167 cls: 'navbar-form form-inline'
6169 //nav navbar-right ml-md-auto
6170 if (this.align === 'right') {
6171 cfg.cls += ' navbar-right ml-md-auto';
6173 cfg.cls += ' navbar-left';
6177 if (this.align === 'right') {
6178 cfg.cls += ' navbar-right ml-md-auto';
6180 cfg.cls += ' mr-auto';
6184 cfg.cls += ' navbar-inverse';
6192 * sets the active Navigation item
6193 * @param {Roo.bootstrap.nav.Item} the new current navitem
6195 setActiveItem : function(item)
6198 Roo.each(this.navItems, function(v){
6203 v.setActive(false, true);
6210 item.setActive(true, true);
6211 this.fireEvent('changed', this, item, prev);
6216 * gets the active Navigation item
6217 * @return {Roo.bootstrap.nav.Item} the current navitem
6219 getActive : function()
6223 Roo.each(this.navItems, function(v){
6234 indexOfNav : function()
6238 Roo.each(this.navItems, function(v,i){
6249 * adds a Navigation item
6250 * @param {Roo.bootstrap.nav.Item} the navitem to add
6252 addItem : function(cfg)
6254 if (this.form && Roo.bootstrap.version == 4) {
6257 var cn = new Roo.bootstrap.nav.Item(cfg);
6259 cn.parentId = this.id;
6260 cn.onRender(this.el, null);
6264 * register a Navigation item
6265 * @param {Roo.bootstrap.nav.Item} the navitem to add
6267 register : function(item)
6269 this.navItems.push( item);
6270 item.navId = this.navId;
6275 * clear all the Navigation item
6278 clearAll : function()
6281 this.el.dom.innerHTML = '';
6284 getNavItem: function(tabId)
6287 Roo.each(this.navItems, function(e) {
6288 if (e.tabId == tabId) {
6298 setActiveNext : function()
6300 var i = this.indexOfNav(this.getActive());
6301 if (i > this.navItems.length) {
6304 this.setActiveItem(this.navItems[i+1]);
6306 setActivePrev : function()
6308 var i = this.indexOfNav(this.getActive());
6312 this.setActiveItem(this.navItems[i-1]);
6314 clearWasActive : function(except) {
6315 Roo.each(this.navItems, function(e) {
6316 if (e.tabId != except.tabId && e.was_active) {
6317 e.was_active = false;
6324 getWasActive : function ()
6327 Roo.each(this.navItems, function(e) {
6342 Roo.apply(Roo.bootstrap.nav.Group, {
6346 * register a Navigation Group
6347 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6349 register : function(navgrp)
6351 this.groups[navgrp.navId] = navgrp;
6355 * fetch a Navigation Group based on the navigation ID
6356 * @param {string} the navgroup to add
6357 * @returns {Roo.bootstrap.nav.Group} the navgroup
6359 get: function(navId) {
6360 if (typeof(this.groups[navId]) == 'undefined') {
6362 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6364 return this.groups[navId] ;
6372 * @class Roo.bootstrap.nav.Item
6373 * @extends Roo.bootstrap.Component
6374 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6375 * @parent Roo.bootstrap.nav.Group
6377 * Bootstrap Navbar.NavItem class
6379 * @cfg {String} href link to
6380 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6381 * @cfg {Boolean} button_outline show and outlined button
6382 * @cfg {String} html content of button
6383 * @cfg {String} badge text inside badge
6384 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6385 * @cfg {String} glyphicon DEPRICATED - use fa
6386 * @cfg {String} icon DEPRICATED - use fa
6387 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6388 * @cfg {Boolean} active Is item active
6389 * @cfg {Boolean} disabled Is item disabled
6390 * @cfg {String} linkcls Link Class
6391 * @cfg {Boolean} preventDefault (true | false) default false
6392 * @cfg {String} tabId the tab that this item activates.
6393 * @cfg {String} tagtype (a|span) render as a href or span?
6394 * @cfg {Boolean} animateRef (true|false) link to element default false
6395 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6398 * Create a new Navbar Item
6399 * @param {Object} config The config object
6401 Roo.bootstrap.nav.Item = function(config){
6402 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6407 * The raw click event for the entire grid.
6408 * @param {Roo.EventObject} e
6413 * Fires when the active item active state changes
6414 * @param {Roo.bootstrap.nav.Item} this
6415 * @param {boolean} state the new state
6421 * Fires when scroll to element
6422 * @param {Roo.bootstrap.nav.Item} this
6423 * @param {Object} options
6424 * @param {Roo.EventObject} e
6432 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6441 preventDefault : false,
6449 button_outline : false,
6453 getAutoCreate : function(){
6460 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6463 cfg.cls += ' active' ;
6465 if (this.disabled) {
6466 cfg.cls += ' disabled';
6470 if (this.button_weight.length) {
6471 cfg.tag = this.href ? 'a' : 'button';
6472 cfg.html = this.html || '';
6473 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6475 cfg.href = this.href;
6478 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6480 cfg.cls += " nav-html";
6483 // menu .. should add dropdown-menu class - so no need for carat..
6485 if (this.badge !== '') {
6487 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6492 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6496 href : this.href || "#",
6497 html: this.html || '',
6501 if (this.tagtype == 'a') {
6502 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6506 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6507 } else if (this.fa) {
6508 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6509 } else if(this.glyphicon) {
6510 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6512 cfg.cn[0].cls += " nav-html";
6516 cfg.cn[0].html += " <span class='caret'></span>";
6520 if (this.badge !== '') {
6521 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6529 onRender : function(ct, position)
6531 // Roo.log("Call onRender: " + this.xtype);
6532 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6536 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6537 this.navLink = this.el.select('.nav-link',true).first();
6538 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6543 initEvents: function()
6545 if (typeof (this.menu) != 'undefined') {
6546 this.menu.parentType = this.xtype;
6547 this.menu.triggerEl = this.el;
6548 this.menu = this.addxtype(Roo.apply({}, this.menu));
6551 this.el.on('click', this.onClick, this);
6553 //if(this.tagtype == 'span'){
6554 // this.el.select('span',true).on('click', this.onClick, this);
6557 // at this point parent should be available..
6558 this.parent().register(this);
6561 onClick : function(e)
6563 if (e.getTarget('.dropdown-menu-item')) {
6564 // did you click on a menu itemm.... - then don't trigger onclick..
6569 this.preventDefault ||
6570 this.href === false ||
6573 //Roo.log("NavItem - prevent Default?");
6577 if (this.disabled) {
6581 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6582 if (tg && tg.transition) {
6583 Roo.log("waiting for the transitionend");
6589 //Roo.log("fire event clicked");
6590 if(this.fireEvent('click', this, e) === false){
6594 if(this.tagtype == 'span'){
6598 //Roo.log(this.href);
6599 var ael = this.el.select('a',true).first();
6602 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6603 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6604 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6605 return; // ignore... - it's a 'hash' to another page.
6607 Roo.log("NavItem - prevent Default?");
6609 this.scrollToElement(e);
6613 var p = this.parent();
6615 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6616 if (typeof(p.setActiveItem) !== 'undefined') {
6617 p.setActiveItem(this);
6621 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6622 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6623 // remove the collapsed menu expand...
6624 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6628 isActive: function () {
6631 setActive : function(state, fire, is_was_active)
6633 if (this.active && !state && this.navId) {
6634 this.was_active = true;
6635 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6637 nv.clearWasActive(this);
6641 this.active = state;
6644 this.el.removeClass('active');
6645 this.navLink ? this.navLink.removeClass('active') : false;
6646 } else if (!this.el.hasClass('active')) {
6648 this.el.addClass('active');
6649 if (Roo.bootstrap.version == 4 && this.navLink ) {
6650 this.navLink.addClass('active');
6655 this.fireEvent('changed', this, state);
6658 // show a panel if it's registered and related..
6660 if (!this.navId || !this.tabId || !state || is_was_active) {
6664 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6668 var pan = tg.getPanelByName(this.tabId);
6672 // if we can not flip to new panel - go back to old nav highlight..
6673 if (false == tg.showPanel(pan)) {
6674 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6676 var onav = nv.getWasActive();
6678 onav.setActive(true, false, true);
6687 // this should not be here...
6688 setDisabled : function(state)
6690 this.disabled = state;
6692 this.el.removeClass('disabled');
6693 } else if (!this.el.hasClass('disabled')) {
6694 this.el.addClass('disabled');
6700 * Fetch the element to display the tooltip on.
6701 * @return {Roo.Element} defaults to this.el
6703 tooltipEl : function()
6705 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6708 scrollToElement : function(e)
6710 var c = document.body;
6713 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6715 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6716 c = document.documentElement;
6719 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6725 var o = target.calcOffsetsTo(c);
6732 this.fireEvent('scrollto', this, options, e);
6734 Roo.get(c).scrollTo('top', options.value, true);
6739 * Set the HTML (text content) of the item
6740 * @param {string} html content for the nav item
6742 setHtml : function(html)
6745 this.htmlEl.dom.innerHTML = html;
6757 * <span> icon </span>
6758 * <span> text </span>
6759 * <span>badge </span>
6763 * @class Roo.bootstrap.nav.SidebarItem
6764 * @extends Roo.bootstrap.nav.Item
6765 * Bootstrap Navbar.NavSidebarItem class
6767 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6768 * {Boolean} open is the menu open
6769 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6770 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6771 * {String} buttonSize (sm|md|lg)the extra classes for the button
6772 * {Boolean} showArrow show arrow next to the text (default true)
6774 * Create a new Navbar Button
6775 * @param {Object} config The config object
6777 Roo.bootstrap.nav.SidebarItem = function(config){
6778 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6783 * The raw click event for the entire grid.
6784 * @param {Roo.EventObject} e
6789 * Fires when the active item active state changes
6790 * @param {Roo.bootstrap.nav.SidebarItem} this
6791 * @param {boolean} state the new state
6799 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6801 badgeWeight : 'default',
6807 buttonWeight : 'default',
6813 getAutoCreate : function(){
6818 href : this.href || '#',
6824 if(this.buttonView){
6827 href : this.href || '#',
6828 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6841 cfg.cls += ' active';
6844 if (this.disabled) {
6845 cfg.cls += ' disabled';
6848 cfg.cls += ' open x-open';
6851 if (this.glyphicon || this.icon) {
6852 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6853 a.cn.push({ tag : 'i', cls : c }) ;
6856 if(!this.buttonView){
6859 html : this.html || ''
6866 if (this.badge !== '') {
6867 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6873 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6876 a.cls += ' dropdown-toggle treeview' ;
6882 initEvents : function()
6884 if (typeof (this.menu) != 'undefined') {
6885 this.menu.parentType = this.xtype;
6886 this.menu.triggerEl = this.el;
6887 this.menu = this.addxtype(Roo.apply({}, this.menu));
6890 this.el.on('click', this.onClick, this);
6892 if(this.badge !== ''){
6893 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6898 onClick : function(e)
6905 if(this.preventDefault){
6909 this.fireEvent('click', this, e);
6912 disable : function()
6914 this.setDisabled(true);
6919 this.setDisabled(false);
6922 setDisabled : function(state)
6924 if(this.disabled == state){
6928 this.disabled = state;
6931 this.el.addClass('disabled');
6935 this.el.removeClass('disabled');
6940 setActive : function(state)
6942 if(this.active == state){
6946 this.active = state;
6949 this.el.addClass('active');
6953 this.el.removeClass('active');
6958 isActive: function ()
6963 setBadge : function(str)
6969 this.badgeEl.dom.innerHTML = str;
6986 * @class Roo.bootstrap.nav.ProgressBar
6987 * @extends Roo.bootstrap.Component
6988 * @children Roo.bootstrap.nav.ProgressBarItem
6989 * Bootstrap NavProgressBar class
6992 * Create a new nav progress bar - a bar indicating step along a process
6993 * @param {Object} config The config object
6996 Roo.bootstrap.nav.ProgressBar = function(config){
6997 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6999 this.bullets = this.bullets || [];
7001 // Roo.bootstrap.nav.ProgressBar.register(this);
7005 * Fires when the active item changes
7006 * @param {Roo.bootstrap.nav.ProgressBar} this
7007 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7008 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7015 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7017 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7018 * Bullets for the Nav Progress bar for the toolbar
7023 getAutoCreate : function()
7025 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7029 cls : 'roo-navigation-bar-group',
7033 cls : 'roo-navigation-top-bar'
7037 cls : 'roo-navigation-bullets-bar',
7041 cls : 'roo-navigation-bar'
7048 cls : 'roo-navigation-bottom-bar'
7058 initEvents: function()
7063 onRender : function(ct, position)
7065 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7067 if(this.bullets.length){
7068 Roo.each(this.bullets, function(b){
7077 addItem : function(cfg)
7079 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7081 item.parentId = this.id;
7082 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7085 var top = new Roo.bootstrap.Element({
7087 cls : 'roo-navigation-bar-text'
7090 var bottom = new Roo.bootstrap.Element({
7092 cls : 'roo-navigation-bar-text'
7095 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7096 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7098 var topText = new Roo.bootstrap.Element({
7100 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7103 var bottomText = new Roo.bootstrap.Element({
7105 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7108 topText.onRender(top.el, null);
7109 bottomText.onRender(bottom.el, null);
7112 item.bottomEl = bottom;
7115 this.barItems.push(item);
7120 getActive : function()
7124 Roo.each(this.barItems, function(v){
7126 if (!v.isActive()) {
7138 setActiveItem : function(item)
7142 Roo.each(this.barItems, function(v){
7143 if (v.rid == item.rid) {
7153 item.setActive(true);
7155 this.fireEvent('changed', this, item, prev);
7158 getBarItem: function(rid)
7162 Roo.each(this.barItems, function(e) {
7174 indexOfItem : function(item)
7178 Roo.each(this.barItems, function(v, i){
7180 if (v.rid != item.rid) {
7191 setActiveNext : function()
7193 var i = this.indexOfItem(this.getActive());
7195 if (i > this.barItems.length) {
7199 this.setActiveItem(this.barItems[i+1]);
7202 setActivePrev : function()
7204 var i = this.indexOfItem(this.getActive());
7210 this.setActiveItem(this.barItems[i-1]);
7215 if(!this.barItems.length){
7219 var width = 100 / this.barItems.length;
7221 Roo.each(this.barItems, function(i){
7222 i.el.setStyle('width', width + '%');
7223 i.topEl.el.setStyle('width', width + '%');
7224 i.bottomEl.el.setStyle('width', width + '%');
7238 * @class Roo.bootstrap.nav.ProgressBarItem
7239 * @extends Roo.bootstrap.Component
7240 * Bootstrap NavProgressBarItem class
7241 * @cfg {String} rid the reference id
7242 * @cfg {Boolean} active (true|false) Is item active default false
7243 * @cfg {Boolean} disabled (true|false) Is item active default false
7244 * @cfg {String} html
7245 * @cfg {String} position (top|bottom) text position default bottom
7246 * @cfg {String} icon show icon instead of number
7249 * Create a new NavProgressBarItem
7250 * @param {Object} config The config object
7252 Roo.bootstrap.nav.ProgressBarItem = function(config){
7253 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7258 * The raw click event for the entire grid.
7259 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7260 * @param {Roo.EventObject} e
7267 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7273 position : 'bottom',
7276 getAutoCreate : function()
7278 var iconCls = 'roo-navigation-bar-item-icon';
7280 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7284 cls: 'roo-navigation-bar-item',
7294 cfg.cls += ' active';
7297 cfg.cls += ' disabled';
7303 disable : function()
7305 this.setDisabled(true);
7310 this.setDisabled(false);
7313 initEvents: function()
7315 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7317 this.iconEl.on('click', this.onClick, this);
7320 onClick : function(e)
7328 if(this.fireEvent('click', this, e) === false){
7332 this.parent().setActiveItem(this);
7335 isActive: function ()
7340 setActive : function(state)
7342 if(this.active == state){
7346 this.active = state;
7349 this.el.addClass('active');
7353 this.el.removeClass('active');
7358 setDisabled : function(state)
7360 if(this.disabled == state){
7364 this.disabled = state;
7367 this.el.addClass('disabled');
7371 this.el.removeClass('disabled');
7374 tooltipEl : function()
7376 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7387 Roo.namespace('Roo.bootstrap.breadcrumb');
7391 * @class Roo.bootstrap.breadcrumb.Nav
7392 * @extends Roo.bootstrap.Component
7393 * Bootstrap Breadcrumb Nav Class
7395 * @children Roo.bootstrap.breadcrumb.Item
7398 * Create a new breadcrumb.Nav
7399 * @param {Object} config The config object
7403 Roo.bootstrap.breadcrumb.Nav = function(config){
7404 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7409 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7411 getAutoCreate : function()
7428 initEvents: function()
7430 this.olEl = this.el.select('ol',true).first();
7432 getChildContainer : function()
7448 * @class Roo.bootstrap.breadcrumb.Nav
7449 * @extends Roo.bootstrap.Component
7450 * @children Roo.bootstrap.Component
7451 * @parent Roo.bootstrap.breadcrumb.Nav
7452 * Bootstrap Breadcrumb Nav Class
7455 * @cfg {String} html the content of the link.
7456 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7457 * @cfg {Boolean} active is it active
7461 * Create a new breadcrumb.Nav
7462 * @param {Object} config The config object
7465 Roo.bootstrap.breadcrumb.Item = function(config){
7466 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7471 * The img click event for the img.
7472 * @param {Roo.EventObject} e
7479 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7484 getAutoCreate : function()
7489 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7491 if (this.href !== false) {
7498 cfg.html = this.html;
7504 initEvents: function()
7507 this.el.select('a', true).first().on('click',this.onClick, this)
7511 onClick : function(e)
7514 this.fireEvent('click',this, e);
7527 * @class Roo.bootstrap.Row
7528 * @extends Roo.bootstrap.Component
7529 * @children Roo.bootstrap.Component
7530 * Bootstrap Row class (contains columns...)
7534 * @param {Object} config The config object
7537 Roo.bootstrap.Row = function(config){
7538 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7541 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7543 getAutoCreate : function(){
7562 * @class Roo.bootstrap.Pagination
7563 * @extends Roo.bootstrap.Component
7564 * @children Roo.bootstrap.Pagination
7565 * Bootstrap Pagination class
7567 * @cfg {String} size (xs|sm|md|lg|xl)
7568 * @cfg {Boolean} inverse
7571 * Create a new Pagination
7572 * @param {Object} config The config object
7575 Roo.bootstrap.Pagination = function(config){
7576 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7579 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7585 getAutoCreate : function(){
7591 cfg.cls += ' inverse';
7597 cfg.cls += " " + this.cls;
7615 * @class Roo.bootstrap.PaginationItem
7616 * @extends Roo.bootstrap.Component
7617 * Bootstrap PaginationItem class
7618 * @cfg {String} html text
7619 * @cfg {String} href the link
7620 * @cfg {Boolean} preventDefault (true | false) default true
7621 * @cfg {Boolean} active (true | false) default false
7622 * @cfg {Boolean} disabled default false
7626 * Create a new PaginationItem
7627 * @param {Object} config The config object
7631 Roo.bootstrap.PaginationItem = function(config){
7632 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7637 * The raw click event for the entire grid.
7638 * @param {Roo.EventObject} e
7644 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7648 preventDefault: true,
7653 getAutoCreate : function(){
7659 href : this.href ? this.href : '#',
7660 html : this.html ? this.html : ''
7670 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7674 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7680 initEvents: function() {
7682 this.el.on('click', this.onClick, this);
7685 onClick : function(e)
7687 Roo.log('PaginationItem on click ');
7688 if(this.preventDefault){
7696 this.fireEvent('click', this, e);
7712 * @class Roo.bootstrap.Slider
7713 * @extends Roo.bootstrap.Component
7714 * Bootstrap Slider class
7717 * Create a new Slider
7718 * @param {Object} config The config object
7721 Roo.bootstrap.Slider = function(config){
7722 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7725 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7727 getAutoCreate : function(){
7731 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7735 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7747 * Ext JS Library 1.1.1
7748 * Copyright(c) 2006-2007, Ext JS, LLC.
7750 * Originally Released Under LGPL - original licence link has changed is not relivant.
7753 * <script type="text/javascript">
7756 * @extends Roo.dd.DDProxy
7757 * @class Roo.grid.SplitDragZone
7758 * Support for Column Header resizing
7760 * @param {Object} config
7763 // This is a support class used internally by the Grid components
7764 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7766 this.view = grid.getView();
7767 this.proxy = this.view.resizeProxy;
7768 Roo.grid.SplitDragZone.superclass.constructor.call(
7771 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7773 dragElId : Roo.id(this.proxy.dom),
7778 this.setHandleElId(Roo.id(hd));
7779 if (hd2 !== false) {
7780 this.setOuterHandleElId(Roo.id(hd2));
7783 this.scroll = false;
7785 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7786 fly: Roo.Element.fly,
7788 b4StartDrag : function(x, y){
7789 this.view.headersDisabled = true;
7790 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7791 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7793 this.proxy.setHeight(h);
7795 // for old system colWidth really stored the actual width?
7796 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7797 // which in reality did not work.. - it worked only for fixed sizes
7798 // for resizable we need to use actual sizes.
7799 var w = this.cm.getColumnWidth(this.cellIndex);
7800 if (!this.view.mainWrap) {
7802 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7807 // this was w-this.grid.minColumnWidth;
7808 // doesnt really make sense? - w = thie curren width or the rendered one?
7809 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7810 this.resetConstraints();
7811 this.setXConstraint(minw, 1000);
7812 this.setYConstraint(0, 0);
7813 this.minX = x - minw;
7814 this.maxX = x + 1000;
7816 if (!this.view.mainWrap) { // this is Bootstrap code..
7817 this.getDragEl().style.display='block';
7820 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7824 handleMouseDown : function(e){
7825 ev = Roo.EventObject.setEvent(e);
7826 var t = this.fly(ev.getTarget());
7827 if(t.hasClass("x-grid-split")){
7828 this.cellIndex = this.view.getCellIndex(t.dom);
7830 this.cm = this.grid.colModel;
7831 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7832 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7837 endDrag : function(e){
7838 this.view.headersDisabled = false;
7839 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7840 var diff = endX - this.startPos;
7842 var w = this.cm.getColumnWidth(this.cellIndex);
7843 if (!this.view.mainWrap) {
7846 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7849 autoOffset : function(){
7854 * Ext JS Library 1.1.1
7855 * Copyright(c) 2006-2007, Ext JS, LLC.
7857 * Originally Released Under LGPL - original licence link has changed is not relivant.
7860 * <script type="text/javascript">
7864 * @class Roo.grid.AbstractSelectionModel
7865 * @extends Roo.util.Observable
7867 * Abstract base class for grid SelectionModels. It provides the interface that should be
7868 * implemented by descendant classes. This class should not be directly instantiated.
7871 Roo.grid.AbstractSelectionModel = function(){
7872 this.locked = false;
7873 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7876 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7877 /** @ignore Called by the grid automatically. Do not call directly. */
7878 init : function(grid){
7884 * Locks the selections.
7891 * Unlocks the selections.
7893 unlock : function(){
7894 this.locked = false;
7898 * Returns true if the selections are locked.
7901 isLocked : function(){
7906 * Ext JS Library 1.1.1
7907 * Copyright(c) 2006-2007, Ext JS, LLC.
7909 * Originally Released Under LGPL - original licence link has changed is not relivant.
7912 * <script type="text/javascript">
7915 * @extends Roo.grid.AbstractSelectionModel
7916 * @class Roo.grid.RowSelectionModel
7917 * The default SelectionModel used by {@link Roo.grid.Grid}.
7918 * It supports multiple selections and keyboard selection/navigation.
7920 * @param {Object} config
7922 Roo.grid.RowSelectionModel = function(config){
7923 Roo.apply(this, config);
7924 this.selections = new Roo.util.MixedCollection(false, function(o){
7929 this.lastActive = false;
7933 * @event selectionchange
7934 * Fires when the selection changes
7935 * @param {SelectionModel} this
7937 "selectionchange" : true,
7939 * @event afterselectionchange
7940 * Fires after the selection changes (eg. by key press or clicking)
7941 * @param {SelectionModel} this
7943 "afterselectionchange" : true,
7945 * @event beforerowselect
7946 * Fires when a row is selected being selected, return false to cancel.
7947 * @param {SelectionModel} this
7948 * @param {Number} rowIndex The selected index
7949 * @param {Boolean} keepExisting False if other selections will be cleared
7951 "beforerowselect" : true,
7954 * Fires when a row is selected.
7955 * @param {SelectionModel} this
7956 * @param {Number} rowIndex The selected index
7957 * @param {Roo.data.Record} r The record
7961 * @event rowdeselect
7962 * Fires when a row is deselected.
7963 * @param {SelectionModel} this
7964 * @param {Number} rowIndex The selected index
7966 "rowdeselect" : true
7968 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7969 this.locked = false;
7972 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7974 * @cfg {Boolean} singleSelect
7975 * True to allow selection of only one row at a time (defaults to false)
7977 singleSelect : false,
7980 initEvents : function(){
7982 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7983 this.grid.on("mousedown", this.handleMouseDown, this);
7984 }else{ // allow click to work like normal
7985 this.grid.on("rowclick", this.handleDragableRowClick, this);
7987 // bootstrap does not have a view..
7988 var view = this.grid.view ? this.grid.view : this.grid;
7989 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7992 this.selectPrevious(e.shiftKey);
7993 }else if(this.last !== false && this.lastActive !== false){
7994 var last = this.last;
7995 this.selectRange(this.last, this.lastActive-1);
7996 view.focusRow(this.lastActive);
8001 this.selectFirstRow();
8003 this.fireEvent("afterselectionchange", this);
8005 "down" : function(e){
8007 this.selectNext(e.shiftKey);
8008 }else if(this.last !== false && this.lastActive !== false){
8009 var last = this.last;
8010 this.selectRange(this.last, this.lastActive+1);
8011 view.focusRow(this.lastActive);
8016 this.selectFirstRow();
8018 this.fireEvent("afterselectionchange", this);
8024 view.on("refresh", this.onRefresh, this);
8025 view.on("rowupdated", this.onRowUpdated, this);
8026 view.on("rowremoved", this.onRemove, this);
8030 onRefresh : function(){
8031 var ds = this.grid.ds, i, v = this.grid.view;
8032 var s = this.selections;
8034 if((i = ds.indexOfId(r.id)) != -1){
8036 s.add(ds.getAt(i)); // updating the selection relate data
8044 onRemove : function(v, index, r){
8045 this.selections.remove(r);
8049 onRowUpdated : function(v, index, r){
8050 if(this.isSelected(r)){
8051 v.onRowSelect(index);
8057 * @param {Array} records The records to select
8058 * @param {Boolean} keepExisting (optional) True to keep existing selections
8060 selectRecords : function(records, keepExisting){
8062 this.clearSelections();
8064 var ds = this.grid.ds;
8065 for(var i = 0, len = records.length; i < len; i++){
8066 this.selectRow(ds.indexOf(records[i]), true);
8071 * Gets the number of selected rows.
8074 getCount : function(){
8075 return this.selections.length;
8079 * Selects the first row in the grid.
8081 selectFirstRow : function(){
8086 * Select the last row.
8087 * @param {Boolean} keepExisting (optional) True to keep existing selections
8089 selectLastRow : function(keepExisting){
8090 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8094 * Selects the row immediately following the last selected row.
8095 * @param {Boolean} keepExisting (optional) True to keep existing selections
8097 selectNext : function(keepExisting){
8098 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8099 this.selectRow(this.last+1, keepExisting);
8100 var view = this.grid.view ? this.grid.view : this.grid;
8101 view.focusRow(this.last);
8106 * Selects the row that precedes the last selected row.
8107 * @param {Boolean} keepExisting (optional) True to keep existing selections
8109 selectPrevious : function(keepExisting){
8111 this.selectRow(this.last-1, keepExisting);
8112 var view = this.grid.view ? this.grid.view : this.grid;
8113 view.focusRow(this.last);
8118 * Returns the selected records
8119 * @return {Array} Array of selected records
8121 getSelections : function(){
8122 return [].concat(this.selections.items);
8126 * Returns the first selected record.
8129 getSelected : function(){
8130 return this.selections.itemAt(0);
8135 * Clears all selections.
8137 clearSelections : function(fast){
8142 var ds = this.grid.ds;
8143 var s = this.selections;
8145 this.deselectRow(ds.indexOfId(r.id));
8149 this.selections.clear();
8158 selectAll : function(){
8162 this.selections.clear();
8163 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8164 this.selectRow(i, true);
8169 * Returns True if there is a selection.
8172 hasSelection : function(){
8173 return this.selections.length > 0;
8177 * Returns True if the specified row is selected.
8178 * @param {Number/Record} record The record or index of the record to check
8181 isSelected : function(index){
8182 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8183 return (r && this.selections.key(r.id) ? true : false);
8187 * Returns True if the specified record id is selected.
8188 * @param {String} id The id of record to check
8191 isIdSelected : function(id){
8192 return (this.selections.key(id) ? true : false);
8196 handleMouseDown : function(e, t)
8198 var view = this.grid.view ? this.grid.view : this.grid;
8200 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8203 if(e.shiftKey && this.last !== false){
8204 var last = this.last;
8205 this.selectRange(last, rowIndex, e.ctrlKey);
8206 this.last = last; // reset the last
8207 view.focusRow(rowIndex);
8209 var isSelected = this.isSelected(rowIndex);
8210 if(e.button !== 0 && isSelected){
8211 view.focusRow(rowIndex);
8212 }else if(e.ctrlKey && isSelected){
8213 this.deselectRow(rowIndex);
8214 }else if(!isSelected){
8215 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8216 view.focusRow(rowIndex);
8219 this.fireEvent("afterselectionchange", this);
8222 handleDragableRowClick : function(grid, rowIndex, e)
8224 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8225 this.selectRow(rowIndex, false);
8226 var view = this.grid.view ? this.grid.view : this.grid;
8227 view.focusRow(rowIndex);
8228 this.fireEvent("afterselectionchange", this);
8233 * Selects multiple rows.
8234 * @param {Array} rows Array of the indexes of the row to select
8235 * @param {Boolean} keepExisting (optional) True to keep existing selections
8237 selectRows : function(rows, keepExisting){
8239 this.clearSelections();
8241 for(var i = 0, len = rows.length; i < len; i++){
8242 this.selectRow(rows[i], true);
8247 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8248 * @param {Number} startRow The index of the first row in the range
8249 * @param {Number} endRow The index of the last row in the range
8250 * @param {Boolean} keepExisting (optional) True to retain existing selections
8252 selectRange : function(startRow, endRow, keepExisting){
8257 this.clearSelections();
8259 if(startRow <= endRow){
8260 for(var i = startRow; i <= endRow; i++){
8261 this.selectRow(i, true);
8264 for(var i = startRow; i >= endRow; i--){
8265 this.selectRow(i, true);
8271 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8272 * @param {Number} startRow The index of the first row in the range
8273 * @param {Number} endRow The index of the last row in the range
8275 deselectRange : function(startRow, endRow, preventViewNotify){
8279 for(var i = startRow; i <= endRow; i++){
8280 this.deselectRow(i, preventViewNotify);
8286 * @param {Number} row The index of the row to select
8287 * @param {Boolean} keepExisting (optional) True to keep existing selections
8289 selectRow : function(index, keepExisting, preventViewNotify){
8290 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8293 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8294 if(!keepExisting || this.singleSelect){
8295 this.clearSelections();
8297 var r = this.grid.ds.getAt(index);
8298 this.selections.add(r);
8299 this.last = this.lastActive = index;
8300 if(!preventViewNotify){
8301 var view = this.grid.view ? this.grid.view : this.grid;
8302 view.onRowSelect(index);
8304 this.fireEvent("rowselect", this, index, r);
8305 this.fireEvent("selectionchange", this);
8311 * @param {Number} row The index of the row to deselect
8313 deselectRow : function(index, preventViewNotify){
8317 if(this.last == index){
8320 if(this.lastActive == index){
8321 this.lastActive = false;
8323 var r = this.grid.ds.getAt(index);
8324 this.selections.remove(r);
8325 if(!preventViewNotify){
8326 var view = this.grid.view ? this.grid.view : this.grid;
8327 view.onRowDeselect(index);
8329 this.fireEvent("rowdeselect", this, index);
8330 this.fireEvent("selectionchange", this);
8334 restoreLast : function(){
8336 this.last = this._last;
8341 acceptsNav : function(row, col, cm){
8342 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8346 onEditorKey : function(field, e){
8347 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8352 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8354 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8356 }else if(k == e.ENTER && !e.ctrlKey){
8360 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8362 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8364 }else if(k == e.ESC){
8368 g.startEditing(newCell[0], newCell[1]);
8373 * Ext JS Library 1.1.1
8374 * Copyright(c) 2006-2007, Ext JS, LLC.
8376 * Originally Released Under LGPL - original licence link has changed is not relivant.
8379 * <script type="text/javascript">
8384 * @class Roo.grid.ColumnModel
8385 * @extends Roo.util.Observable
8386 * This is the default implementation of a ColumnModel used by the Grid. It defines
8387 * the columns in the grid.
8390 var colModel = new Roo.grid.ColumnModel([
8391 {header: "Ticker", width: 60, sortable: true, locked: true},
8392 {header: "Company Name", width: 150, sortable: true},
8393 {header: "Market Cap.", width: 100, sortable: true},
8394 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8395 {header: "Employees", width: 100, sortable: true, resizable: false}
8400 * The config options listed for this class are options which may appear in each
8401 * individual column definition.
8402 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8404 * @param {Object} config An Array of column config objects. See this class's
8405 * config objects for details.
8407 Roo.grid.ColumnModel = function(config){
8409 * The config passed into the constructor
8411 this.config = []; //config;
8414 // if no id, create one
8415 // if the column does not have a dataIndex mapping,
8416 // map it to the order it is in the config
8417 for(var i = 0, len = config.length; i < len; i++){
8418 this.addColumn(config[i]);
8423 * The width of columns which have no width specified (defaults to 100)
8426 this.defaultWidth = 100;
8429 * Default sortable of columns which have no sortable specified (defaults to false)
8432 this.defaultSortable = false;
8436 * @event widthchange
8437 * Fires when the width of a column changes.
8438 * @param {ColumnModel} this
8439 * @param {Number} columnIndex The column index
8440 * @param {Number} newWidth The new width
8442 "widthchange": true,
8444 * @event headerchange
8445 * Fires when the text of a header changes.
8446 * @param {ColumnModel} this
8447 * @param {Number} columnIndex The column index
8448 * @param {Number} newText The new header text
8450 "headerchange": true,
8452 * @event hiddenchange
8453 * Fires when a column is hidden or "unhidden".
8454 * @param {ColumnModel} this
8455 * @param {Number} columnIndex The column index
8456 * @param {Boolean} hidden true if hidden, false otherwise
8458 "hiddenchange": true,
8460 * @event columnmoved
8461 * Fires when a column is moved.
8462 * @param {ColumnModel} this
8463 * @param {Number} oldIndex
8464 * @param {Number} newIndex
8466 "columnmoved" : true,
8468 * @event columlockchange
8469 * Fires when a column's locked state is changed
8470 * @param {ColumnModel} this
8471 * @param {Number} colIndex
8472 * @param {Boolean} locked true if locked
8474 "columnlockchange" : true
8476 Roo.grid.ColumnModel.superclass.constructor.call(this);
8478 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8480 * @cfg {String} header [required] The header text to display in the Grid view.
8483 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8486 * @cfg {String} smHeader Header at Bootsrap Small width
8489 * @cfg {String} mdHeader Header at Bootsrap Medium width
8492 * @cfg {String} lgHeader Header at Bootsrap Large width
8495 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8498 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8499 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8500 * specified, the column's index is used as an index into the Record's data Array.
8503 * @cfg {Number} width The initial width in pixels of the column. Using this
8504 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8507 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8508 * Defaults to the value of the {@link #defaultSortable} property.
8509 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8512 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8515 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8518 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8521 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8524 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8525 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8526 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8527 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8530 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8533 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8536 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8539 * @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)
8542 * @cfg {String} tooltip mouse over tooltip text
8545 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8548 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8551 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8554 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8557 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8560 * Returns the id of the column at the specified index.
8561 * @param {Number} index The column index
8562 * @return {String} the id
8564 getColumnId : function(index){
8565 return this.config[index].id;
8569 * Returns the column for a specified id.
8570 * @param {String} id The column id
8571 * @return {Object} the column
8573 getColumnById : function(id){
8574 return this.lookup[id];
8579 * Returns the column Object for a specified dataIndex.
8580 * @param {String} dataIndex The column dataIndex
8581 * @return {Object|Boolean} the column or false if not found
8583 getColumnByDataIndex: function(dataIndex){
8584 var index = this.findColumnIndex(dataIndex);
8585 return index > -1 ? this.config[index] : false;
8589 * Returns the index for a specified column id.
8590 * @param {String} id The column id
8591 * @return {Number} the index, or -1 if not found
8593 getIndexById : function(id){
8594 for(var i = 0, len = this.config.length; i < len; i++){
8595 if(this.config[i].id == id){
8603 * Returns the index for a specified column dataIndex.
8604 * @param {String} dataIndex The column dataIndex
8605 * @return {Number} the index, or -1 if not found
8608 findColumnIndex : function(dataIndex){
8609 for(var i = 0, len = this.config.length; i < len; i++){
8610 if(this.config[i].dataIndex == dataIndex){
8618 moveColumn : function(oldIndex, newIndex){
8619 var c = this.config[oldIndex];
8620 this.config.splice(oldIndex, 1);
8621 this.config.splice(newIndex, 0, c);
8622 this.dataMap = null;
8623 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8626 isLocked : function(colIndex){
8627 return this.config[colIndex].locked === true;
8630 setLocked : function(colIndex, value, suppressEvent){
8631 if(this.isLocked(colIndex) == value){
8634 this.config[colIndex].locked = value;
8636 this.fireEvent("columnlockchange", this, colIndex, value);
8640 getTotalLockedWidth : function(){
8642 for(var i = 0; i < this.config.length; i++){
8643 if(this.isLocked(i) && !this.isHidden(i)){
8644 this.totalWidth += this.getColumnWidth(i);
8650 getLockedCount : function(){
8651 for(var i = 0, len = this.config.length; i < len; i++){
8652 if(!this.isLocked(i)){
8657 return this.config.length;
8661 * Returns the number of columns.
8664 getColumnCount : function(visibleOnly){
8665 if(visibleOnly === true){
8667 for(var i = 0, len = this.config.length; i < len; i++){
8668 if(!this.isHidden(i)){
8674 return this.config.length;
8678 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8679 * @param {Function} fn
8680 * @param {Object} scope (optional)
8681 * @return {Array} result
8683 getColumnsBy : function(fn, scope){
8685 for(var i = 0, len = this.config.length; i < len; i++){
8686 var c = this.config[i];
8687 if(fn.call(scope||this, c, i) === true){
8695 * Returns true if the specified column is sortable.
8696 * @param {Number} col The column index
8699 isSortable : function(col){
8700 if(typeof this.config[col].sortable == "undefined"){
8701 return this.defaultSortable;
8703 return this.config[col].sortable;
8707 * Returns the rendering (formatting) function defined for the column.
8708 * @param {Number} col The column index.
8709 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8711 getRenderer : function(col){
8712 if(!this.config[col].renderer){
8713 return Roo.grid.ColumnModel.defaultRenderer;
8715 return this.config[col].renderer;
8719 * Sets the rendering (formatting) function for a column.
8720 * @param {Number} col The column index
8721 * @param {Function} fn The function to use to process the cell's raw data
8722 * to return HTML markup for the grid view. The render function is called with
8723 * the following parameters:<ul>
8724 * <li>Data value.</li>
8725 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8726 * <li>css A CSS style string to apply to the table cell.</li>
8727 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8728 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8729 * <li>Row index</li>
8730 * <li>Column index</li>
8731 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8733 setRenderer : function(col, fn){
8734 this.config[col].renderer = fn;
8738 * Returns the width for the specified column.
8739 * @param {Number} col The column index
8740 * @param (optional) {String} gridSize bootstrap width size.
8743 getColumnWidth : function(col, gridSize)
8745 var cfg = this.config[col];
8747 if (typeof(gridSize) == 'undefined') {
8748 return cfg.width * 1 || this.defaultWidth;
8750 if (gridSize === false) { // if we set it..
8751 return cfg.width || false;
8753 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8755 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8756 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8759 return cfg[ sizes[i] ];
8766 * Sets the width for a column.
8767 * @param {Number} col The column index
8768 * @param {Number} width The new width
8770 setColumnWidth : function(col, width, suppressEvent){
8771 this.config[col].width = width;
8772 this.totalWidth = null;
8774 this.fireEvent("widthchange", this, col, width);
8779 * Returns the total width of all columns.
8780 * @param {Boolean} includeHidden True to include hidden column widths
8783 getTotalWidth : function(includeHidden){
8784 if(!this.totalWidth){
8785 this.totalWidth = 0;
8786 for(var i = 0, len = this.config.length; i < len; i++){
8787 if(includeHidden || !this.isHidden(i)){
8788 this.totalWidth += this.getColumnWidth(i);
8792 return this.totalWidth;
8796 * Returns the header for the specified column.
8797 * @param {Number} col The column index
8800 getColumnHeader : function(col){
8801 return this.config[col].header;
8805 * Sets the header for a column.
8806 * @param {Number} col The column index
8807 * @param {String} header The new header
8809 setColumnHeader : function(col, header){
8810 this.config[col].header = header;
8811 this.fireEvent("headerchange", this, col, header);
8815 * Returns the tooltip for the specified column.
8816 * @param {Number} col The column index
8819 getColumnTooltip : function(col){
8820 return this.config[col].tooltip;
8823 * Sets the tooltip for a column.
8824 * @param {Number} col The column index
8825 * @param {String} tooltip The new tooltip
8827 setColumnTooltip : function(col, tooltip){
8828 this.config[col].tooltip = tooltip;
8832 * Returns the dataIndex for the specified column.
8833 * @param {Number} col The column index
8836 getDataIndex : function(col){
8837 return this.config[col].dataIndex;
8841 * Sets the dataIndex for a column.
8842 * @param {Number} col The column index
8843 * @param {Number} dataIndex The new dataIndex
8845 setDataIndex : function(col, dataIndex){
8846 this.config[col].dataIndex = dataIndex;
8852 * Returns true if the cell is editable.
8853 * @param {Number} colIndex The column index
8854 * @param {Number} rowIndex The row index - this is nto actually used..?
8857 isCellEditable : function(colIndex, rowIndex){
8858 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8862 * Returns the editor defined for the cell/column.
8863 * return false or null to disable editing.
8864 * @param {Number} colIndex The column index
8865 * @param {Number} rowIndex The row index
8868 getCellEditor : function(colIndex, rowIndex){
8869 return this.config[colIndex].editor;
8873 * Sets if a column is editable.
8874 * @param {Number} col The column index
8875 * @param {Boolean} editable True if the column is editable
8877 setEditable : function(col, editable){
8878 this.config[col].editable = editable;
8883 * Returns true if the column is hidden.
8884 * @param {Number} colIndex The column index
8887 isHidden : function(colIndex){
8888 return this.config[colIndex].hidden;
8893 * Returns true if the column width cannot be changed
8895 isFixed : function(colIndex){
8896 return this.config[colIndex].fixed;
8900 * Returns true if the column can be resized
8903 isResizable : function(colIndex){
8904 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8907 * Sets if a column is hidden.
8908 * @param {Number} colIndex The column index
8909 * @param {Boolean} hidden True if the column is hidden
8911 setHidden : function(colIndex, hidden){
8912 this.config[colIndex].hidden = hidden;
8913 this.totalWidth = null;
8914 this.fireEvent("hiddenchange", this, colIndex, hidden);
8918 * Sets the editor for a column.
8919 * @param {Number} col The column index
8920 * @param {Object} editor The editor object
8922 setEditor : function(col, editor){
8923 this.config[col].editor = editor;
8926 * Add a column (experimental...) - defaults to adding to the end..
8927 * @param {Object} config
8929 addColumn : function(c)
8932 var i = this.config.length;
8935 if(typeof c.dataIndex == "undefined"){
8938 if(typeof c.renderer == "string"){
8939 c.renderer = Roo.util.Format[c.renderer];
8941 if(typeof c.id == "undefined"){
8944 if(c.editor && c.editor.xtype){
8945 c.editor = Roo.factory(c.editor, Roo.grid);
8947 if(c.editor && c.editor.isFormField){
8948 c.editor = new Roo.grid.GridEditor(c.editor);
8950 this.lookup[c.id] = c;
8955 Roo.grid.ColumnModel.defaultRenderer = function(value)
8957 if(typeof value == "object") {
8960 if(typeof value == "string" && value.length < 1){
8964 return String.format("{0}", value);
8967 // Alias for backwards compatibility
8968 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8971 * Ext JS Library 1.1.1
8972 * Copyright(c) 2006-2007, Ext JS, LLC.
8974 * Originally Released Under LGPL - original licence link has changed is not relivant.
8977 * <script type="text/javascript">
8981 * @class Roo.LoadMask
8982 * A simple utility class for generically masking elements while loading data. If the element being masked has
8983 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8984 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8985 * element's UpdateManager load indicator and will be destroyed after the initial load.
8987 * Create a new LoadMask
8988 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8989 * @param {Object} config The config object
8991 Roo.LoadMask = function(el, config){
8992 this.el = Roo.get(el);
8993 Roo.apply(this, config);
8995 this.store.on('beforeload', this.onBeforeLoad, this);
8996 this.store.on('load', this.onLoad, this);
8997 this.store.on('loadexception', this.onLoadException, this);
8998 this.removeMask = false;
9000 var um = this.el.getUpdateManager();
9001 um.showLoadIndicator = false; // disable the default indicator
9002 um.on('beforeupdate', this.onBeforeLoad, this);
9003 um.on('update', this.onLoad, this);
9004 um.on('failure', this.onLoad, this);
9005 this.removeMask = true;
9009 Roo.LoadMask.prototype = {
9011 * @cfg {Boolean} removeMask
9012 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9013 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9018 * The text to display in a centered loading message box (defaults to 'Loading...')
9022 * @cfg {String} msgCls
9023 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9025 msgCls : 'x-mask-loading',
9028 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9034 * Disables the mask to prevent it from being displayed
9036 disable : function(){
9037 this.disabled = true;
9041 * Enables the mask so that it can be displayed
9043 enable : function(){
9044 this.disabled = false;
9047 onLoadException : function()
9051 if (typeof(arguments[3]) != 'undefined') {
9052 Roo.MessageBox.alert("Error loading",arguments[3]);
9056 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9057 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9064 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9069 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9073 onBeforeLoad : function(){
9075 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9080 destroy : function(){
9082 this.store.un('beforeload', this.onBeforeLoad, this);
9083 this.store.un('load', this.onLoad, this);
9084 this.store.un('loadexception', this.onLoadException, this);
9086 var um = this.el.getUpdateManager();
9087 um.un('beforeupdate', this.onBeforeLoad, this);
9088 um.un('update', this.onLoad, this);
9089 um.un('failure', this.onLoad, this);
9093 * @class Roo.bootstrap.Table
9095 * @extends Roo.bootstrap.Component
9096 * @children Roo.bootstrap.TableBody
9097 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9098 * Similar to Roo.grid.Grid
9100 var table = Roo.factory({
9102 xns : Roo.bootstrap,
9103 autoSizeColumns: true,
9110 sortInfo : { direction : 'ASC', field: 'name' },
9112 xtype : 'HttpProxy',
9115 url : 'https://example.com/some.data.url.json'
9118 xtype : 'JsonReader',
9120 fields : [ 'id', 'name', whatever' ],
9127 xtype : 'ColumnModel',
9131 dataIndex : 'is_in_group',
9134 renderer : function(v, x , r) {
9136 return String.format("{0}", v)
9142 xtype : 'RowSelectionModel',
9143 xns : Roo.bootstrap.Table
9144 // you can add listeners to catch selection change here....
9150 grid.render(Roo.get("some-div"));
9153 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9158 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9159 * @cfg {Roo.data.Store} store The data store to use
9160 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9162 * @cfg {String} cls table class
9165 * @cfg {string} empty_results Text to display for no results
9166 * @cfg {boolean} striped Should the rows be alternative striped
9167 * @cfg {boolean} bordered Add borders to the table
9168 * @cfg {boolean} hover Add hover highlighting
9169 * @cfg {boolean} condensed Format condensed
9170 * @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,
9171 * also adds table-responsive (see bootstrap docs for details)
9172 * @cfg {Boolean} loadMask (true|false) default false
9173 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9174 * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9175 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9176 * @cfg {Boolean} rowSelection (true|false) default false
9177 * @cfg {Boolean} cellSelection (true|false) default false
9178 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9179 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9180 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9181 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9182 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9183 * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9186 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9189 * Create a new Table
9190 * @param {Object} config The config object
9193 Roo.bootstrap.Table = function(config)
9195 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9198 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9199 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9200 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9201 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9203 this.view = this; // compat with grid.
9205 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9207 this.sm.grid = this;
9208 this.selModel = Roo.factory(this.sm, Roo.grid);
9209 this.sm = this.selModel;
9210 this.sm.xmodule = this.xmodule || false;
9213 if (this.cm && typeof(this.cm.config) == 'undefined') {
9214 this.colModel = new Roo.grid.ColumnModel(this.cm);
9215 this.cm = this.colModel;
9216 this.cm.xmodule = this.xmodule || false;
9219 this.store= Roo.factory(this.store, Roo.data);
9220 this.ds = this.store;
9221 this.ds.xmodule = this.xmodule || false;
9224 if (this.footer && this.store) {
9225 this.footer.dataSource = this.ds;
9226 this.footer = Roo.factory(this.footer);
9233 * Fires when a cell is clicked
9234 * @param {Roo.bootstrap.Table} this
9235 * @param {Roo.Element} el
9236 * @param {Number} rowIndex
9237 * @param {Number} columnIndex
9238 * @param {Roo.EventObject} e
9242 * @event celldblclick
9243 * Fires when a cell is double clicked
9244 * @param {Roo.bootstrap.Table} this
9245 * @param {Roo.Element} el
9246 * @param {Number} rowIndex
9247 * @param {Number} columnIndex
9248 * @param {Roo.EventObject} e
9250 "celldblclick" : true,
9253 * Fires when a row is clicked
9254 * @param {Roo.bootstrap.Table} this
9255 * @param {Roo.Element} el
9256 * @param {Number} rowIndex
9257 * @param {Roo.EventObject} e
9261 * @event rowdblclick
9262 * Fires when a row is double clicked
9263 * @param {Roo.bootstrap.Table} this
9264 * @param {Roo.Element} el
9265 * @param {Number} rowIndex
9266 * @param {Roo.EventObject} e
9268 "rowdblclick" : true,
9271 * Fires when a mouseover occur
9272 * @param {Roo.bootstrap.Table} this
9273 * @param {Roo.Element} el
9274 * @param {Number} rowIndex
9275 * @param {Number} columnIndex
9276 * @param {Roo.EventObject} e
9281 * Fires when a mouseout occur
9282 * @param {Roo.bootstrap.Table} this
9283 * @param {Roo.Element} el
9284 * @param {Number} rowIndex
9285 * @param {Number} columnIndex
9286 * @param {Roo.EventObject} e
9291 * Fires when a row is rendered, so you can change add a style to it.
9292 * @param {Roo.bootstrap.Table} this
9293 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9297 * @event rowsrendered
9298 * Fires when all the rows have been rendered
9299 * @param {Roo.bootstrap.Table} this
9301 'rowsrendered' : true,
9303 * @event contextmenu
9304 * The raw contextmenu event for the entire grid.
9305 * @param {Roo.EventObject} e
9307 "contextmenu" : true,
9309 * @event rowcontextmenu
9310 * Fires when a row is right clicked
9311 * @param {Roo.bootstrap.Table} this
9312 * @param {Number} rowIndex
9313 * @param {Roo.EventObject} e
9315 "rowcontextmenu" : true,
9317 * @event cellcontextmenu
9318 * Fires when a cell is right clicked
9319 * @param {Roo.bootstrap.Table} this
9320 * @param {Number} rowIndex
9321 * @param {Number} cellIndex
9322 * @param {Roo.EventObject} e
9324 "cellcontextmenu" : true,
9326 * @event headercontextmenu
9327 * Fires when a header is right clicked
9328 * @param {Roo.bootstrap.Table} this
9329 * @param {Number} columnIndex
9330 * @param {Roo.EventObject} e
9332 "headercontextmenu" : true,
9335 * The raw mousedown event for the entire grid.
9336 * @param {Roo.EventObject} e
9343 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9361 enableColumnResize: true,
9362 disableAutoSize: false,
9364 rowSelection : false,
9365 cellSelection : false,
9368 minColumnWidth : 50,
9370 // Roo.Element - the tbody
9371 bodyEl: false, // <tbody> Roo.Element - thead element
9372 headEl: false, // <thead> Roo.Element - thead element
9373 resizeProxy : false, // proxy element for dragging?
9377 container: false, // used by gridpanel...
9383 auto_hide_footer : false,
9385 view: false, // actually points to this..
9387 getAutoCreate : function()
9389 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9396 // this get's auto added by panel.Grid
9397 if (this.scrollBody) {
9398 cfg.cls += ' table-body-fixed';
9401 cfg.cls += ' table-striped';
9405 cfg.cls += ' table-hover';
9407 if (this.bordered) {
9408 cfg.cls += ' table-bordered';
9410 if (this.condensed) {
9411 cfg.cls += ' table-condensed';
9414 if (this.responsive) {
9415 cfg.cls += ' table-responsive';
9419 cfg.cls+= ' ' +this.cls;
9425 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9428 if(this.store || this.cm){
9429 if(this.headerShow){
9430 cfg.cn.push(this.renderHeader());
9433 cfg.cn.push(this.renderBody());
9435 if(this.footerShow || this.footerRow){
9436 cfg.cn.push(this.renderFooter());
9439 // where does this come from?
9440 //cfg.cls+= ' TableGrid';
9443 return { cn : [ cfg ] };
9446 initEvents : function()
9448 if(!this.store || !this.cm){
9451 if (this.selModel) {
9452 this.selModel.initEvents();
9456 //Roo.log('initEvents with ds!!!!');
9458 this.bodyEl = this.el.select('tbody', true).first();
9459 this.headEl = this.el.select('thead', true).first();
9460 this.mainFoot = this.el.select('tfoot', true).first();
9465 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9466 e.on('click', this.sort, this);
9470 // why is this done????? = it breaks dialogs??
9471 //this.parent().el.setStyle('position', 'relative');
9475 this.footer.parentId = this.id;
9476 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9479 this.el.select('tfoot tr td').first().addClass('hide');
9484 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9487 this.store.on('load', this.onLoad, this);
9488 this.store.on('beforeload', this.onBeforeLoad, this);
9489 this.store.on('update', this.onUpdate, this);
9490 this.store.on('add', this.onAdd, this);
9491 this.store.on("clear", this.clear, this);
9493 this.el.on("contextmenu", this.onContextMenu, this);
9496 this.cm.on("headerchange", this.onHeaderChange, this);
9497 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9499 //?? does bodyEl get replaced on render?
9500 this.bodyEl.on("click", this.onClick, this);
9501 this.bodyEl.on("dblclick", this.onDblClick, this);
9502 this.bodyEl.on('scroll', this.onBodyScroll, this);
9504 // guessing mainbody will work - this relays usually caught by selmodel at present.
9505 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9508 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9511 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9512 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9517 // Compatibility with grid - we implement all the view features at present.
9518 getView : function()
9523 initCSS : function()
9525 if(this.disableAutoSize) {
9529 var cm = this.cm, styles = [];
9530 this.CSS.removeStyleSheet(this.id + '-cssrules');
9531 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9532 // we can honour xs/sm/md/xl as widths...
9533 // we first have to decide what widht we are currently at...
9534 var sz = Roo.getGridSize();
9538 var cols = []; // visable cols.
9540 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9541 var w = cm.getColumnWidth(i, false);
9543 cols.push( { rel : false, abs : 0 });
9547 cols.push( { rel : false, abs : w });
9549 last = i; // not really..
9552 var w = cm.getColumnWidth(i, sz);
9557 cols.push( { rel : w, abs : false });
9560 var avail = this.bodyEl.dom.clientWidth - total_abs;
9562 var unitWidth = Math.floor(avail / total);
9563 var rem = avail - (unitWidth * total);
9565 var hidden, width, pos = 0 , splithide , left;
9566 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9568 hidden = 'display:none;';
9570 width = 'width:0px;';
9572 if(!cm.isHidden(i)){
9576 // we can honour xs/sm/md/xl ?
9577 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9579 hidden = 'display:none;';
9581 // width should return a small number...
9583 w+=rem; // add the remaining with..
9586 left = "left:" + (pos -4) + "px;";
9587 width = "width:" + w+ "px;";
9590 if (this.responsive) {
9593 hidden = cm.isHidden(i) ? 'display:none;' : '';
9594 splithide = 'display: none;';
9597 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9600 splithide = 'display:none;';
9603 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9604 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9605 // this is the popover version..
9606 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9611 //Roo.log(styles.join(''));
9612 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9618 onContextMenu : function(e, t)
9620 this.processEvent("contextmenu", e);
9623 processEvent : function(name, e)
9625 if (name != 'touchstart' ) {
9626 this.fireEvent(name, e);
9629 var t = e.getTarget();
9631 var cell = Roo.get(t);
9637 if(cell.findParent('tfoot', false, true)){
9641 if(cell.findParent('thead', false, true)){
9643 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9644 cell = Roo.get(t).findParent('th', false, true);
9646 Roo.log("failed to find th in thead?");
9647 Roo.log(e.getTarget());
9652 var cellIndex = cell.dom.cellIndex;
9654 var ename = name == 'touchstart' ? 'click' : name;
9655 this.fireEvent("header" + ename, this, cellIndex, e);
9660 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9661 cell = Roo.get(t).findParent('td', false, true);
9663 Roo.log("failed to find th in tbody?");
9664 Roo.log(e.getTarget());
9669 var row = cell.findParent('tr', false, true);
9670 var cellIndex = cell.dom.cellIndex;
9671 var rowIndex = row.dom.rowIndex - 1;
9675 this.fireEvent("row" + name, this, rowIndex, e);
9679 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9685 onMouseover : function(e, el)
9687 var cell = Roo.get(el);
9693 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9694 cell = cell.findParent('td', false, true);
9697 var row = cell.findParent('tr', false, true);
9698 var cellIndex = cell.dom.cellIndex;
9699 var rowIndex = row.dom.rowIndex - 1; // start from 0
9701 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9705 onMouseout : function(e, el)
9707 var cell = Roo.get(el);
9713 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9714 cell = cell.findParent('td', false, true);
9717 var row = cell.findParent('tr', false, true);
9718 var cellIndex = cell.dom.cellIndex;
9719 var rowIndex = row.dom.rowIndex - 1; // start from 0
9721 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9725 onClick : function(e, el)
9727 var cell = Roo.get(el);
9729 if(!cell || (!this.cellSelection && !this.rowSelection)){
9733 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9734 cell = cell.findParent('td', false, true);
9737 if(!cell || typeof(cell) == 'undefined'){
9741 var row = cell.findParent('tr', false, true);
9743 if(!row || typeof(row) == 'undefined'){
9747 var cellIndex = cell.dom.cellIndex;
9748 var rowIndex = this.getRowIndex(row);
9750 // why??? - should these not be based on SelectionModel?
9751 //if(this.cellSelection){
9752 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9755 //if(this.rowSelection){
9756 this.fireEvent('rowclick', this, row, rowIndex, e);
9761 onDblClick : function(e,el)
9763 var cell = Roo.get(el);
9765 if(!cell || (!this.cellSelection && !this.rowSelection)){
9769 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9770 cell = cell.findParent('td', false, true);
9773 if(!cell || typeof(cell) == 'undefined'){
9777 var row = cell.findParent('tr', false, true);
9779 if(!row || typeof(row) == 'undefined'){
9783 var cellIndex = cell.dom.cellIndex;
9784 var rowIndex = this.getRowIndex(row);
9786 if(this.cellSelection){
9787 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9790 if(this.rowSelection){
9791 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9794 findRowIndex : function(el)
9796 var cell = Roo.get(el);
9800 var row = cell.findParent('tr', false, true);
9802 if(!row || typeof(row) == 'undefined'){
9805 return this.getRowIndex(row);
9807 sort : function(e,el)
9809 var col = Roo.get(el);
9811 if(!col.hasClass('sortable')){
9815 var sort = col.attr('sort');
9818 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9822 this.store.sortInfo = {field : sort, direction : dir};
9825 Roo.log("calling footer first");
9826 this.footer.onClick('first');
9829 this.store.load({ params : { start : 0 } });
9833 renderHeader : function()
9841 this.totalWidth = 0;
9843 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9845 var config = cm.config[i];
9849 cls : 'x-hcol-' + i,
9852 html: cm.getColumnHeader(i)
9855 var tooltip = cm.getColumnTooltip(i);
9857 c.tooltip = tooltip;
9863 if(typeof(config.sortable) != 'undefined' && config.sortable){
9864 c.cls += ' sortable';
9865 c.html = '<i class="fa"></i>' + c.html;
9868 // could use BS4 hidden-..-down
9870 if(typeof(config.lgHeader) != 'undefined'){
9871 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9874 if(typeof(config.mdHeader) != 'undefined'){
9875 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9878 if(typeof(config.smHeader) != 'undefined'){
9879 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9882 if(typeof(config.xsHeader) != 'undefined'){
9883 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9890 if(typeof(config.tooltip) != 'undefined'){
9891 c.tooltip = config.tooltip;
9894 if(typeof(config.colspan) != 'undefined'){
9895 c.colspan = config.colspan;
9898 // hidden is handled by CSS now
9900 if(typeof(config.dataIndex) != 'undefined'){
9901 c.sort = config.dataIndex;
9906 if(typeof(config.align) != 'undefined' && config.align.length){
9907 c.style += ' text-align:' + config.align + ';';
9910 /* width is done in CSS
9911 *if(typeof(config.width) != 'undefined'){
9912 c.style += ' width:' + config.width + 'px;';
9913 this.totalWidth += config.width;
9915 this.totalWidth += 100; // assume minimum of 100 per column?
9919 if(typeof(config.cls) != 'undefined'){
9920 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9922 // this is the bit that doesnt reall work at all...
9924 if (this.responsive) {
9927 ['xs','sm','md','lg'].map(function(size){
9929 if(typeof(config[size]) == 'undefined'){
9933 if (!config[size]) { // 0 = hidden
9934 // BS 4 '0' is treated as hide that column and below.
9935 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9939 c.cls += ' col-' + size + '-' + config[size] + (
9940 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9948 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9959 renderBody : function()
9969 colspan : this.cm.getColumnCount()
9979 renderFooter : function()
9989 colspan : this.cm.getColumnCount()
10001 // Roo.log('ds onload');
10006 var ds = this.store;
10008 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10009 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10010 if (_this.store.sortInfo) {
10012 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10013 e.select('i', true).addClass(['fa-arrow-up']);
10016 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10017 e.select('i', true).addClass(['fa-arrow-down']);
10022 var tbody = this.bodyEl;
10024 if(ds.getCount() > 0){
10025 ds.data.each(function(d,rowIndex){
10026 var row = this.renderRow(cm, ds, rowIndex);
10028 tbody.createChild(row);
10032 if(row.cellObjects.length){
10033 Roo.each(row.cellObjects, function(r){
10034 _this.renderCellObject(r);
10039 } else if (this.empty_results.length) {
10040 this.el.mask(this.empty_results, 'no-spinner');
10043 var tfoot = this.el.select('tfoot', true).first();
10045 if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10047 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10049 var total = this.ds.getTotalCount();
10051 if(this.footer.pageSize < total){
10052 this.mainFoot.show();
10056 if(!this.footerShow && this.footerRow) {
10063 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10064 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10067 cls : ' x-fcol-' + i,
10075 tfoot.dom.innerHTML = '';
10077 tfoot.createChild(tr);
10080 Roo.each(this.el.select('tbody td', true).elements, function(e){
10081 e.on('mouseover', _this.onMouseover, _this);
10084 Roo.each(this.el.select('tbody td', true).elements, function(e){
10085 e.on('mouseout', _this.onMouseout, _this);
10087 this.fireEvent('rowsrendered', this);
10091 this.initCSS(); /// resize cols
10097 onUpdate : function(ds,record)
10099 this.refreshRow(record);
10103 onRemove : function(ds, record, index, isUpdate){
10104 if(isUpdate !== true){
10105 this.fireEvent("beforerowremoved", this, index, record);
10107 var bt = this.bodyEl.dom;
10109 var rows = this.el.select('tbody > tr', true).elements;
10111 if(typeof(rows[index]) != 'undefined'){
10112 bt.removeChild(rows[index].dom);
10115 // if(bt.rows[index]){
10116 // bt.removeChild(bt.rows[index]);
10119 if(isUpdate !== true){
10120 //this.stripeRows(index);
10121 //this.syncRowHeights(index, index);
10123 this.fireEvent("rowremoved", this, index, record);
10127 onAdd : function(ds, records, rowIndex)
10129 //Roo.log('on Add called');
10130 // - note this does not handle multiple adding very well..
10131 var bt = this.bodyEl.dom;
10132 for (var i =0 ; i < records.length;i++) {
10133 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10134 //Roo.log(records[i]);
10135 //Roo.log(this.store.getAt(rowIndex+i));
10136 this.insertRow(this.store, rowIndex + i, false);
10143 refreshRow : function(record){
10144 var ds = this.store, index;
10145 if(typeof record == 'number'){
10147 record = ds.getAt(index);
10149 index = ds.indexOf(record);
10151 return; // should not happen - but seems to
10154 this.insertRow(ds, index, true);
10156 this.onRemove(ds, record, index+1, true);
10158 //this.syncRowHeights(index, index);
10160 this.fireEvent("rowupdated", this, index, record);
10162 // private - called by RowSelection
10163 onRowSelect : function(rowIndex){
10164 var row = this.getRowDom(rowIndex);
10165 row.addClass(['bg-info','info']);
10167 // private - called by RowSelection
10168 onRowDeselect : function(rowIndex)
10170 if (rowIndex < 0) {
10173 var row = this.getRowDom(rowIndex);
10174 row.removeClass(['bg-info','info']);
10177 * Focuses the specified row.
10178 * @param {Number} row The row index
10180 focusRow : function(row)
10182 //Roo.log('GridView.focusRow');
10183 var x = this.bodyEl.dom.scrollLeft;
10184 this.focusCell(row, 0, false);
10185 this.bodyEl.dom.scrollLeft = x;
10189 * Focuses the specified cell.
10190 * @param {Number} row The row index
10191 * @param {Number} col The column index
10192 * @param {Boolean} hscroll false to disable horizontal scrolling
10194 focusCell : function(row, col, hscroll)
10196 //Roo.log('GridView.focusCell');
10197 var el = this.ensureVisible(row, col, hscroll);
10198 // not sure what focusEL achives = it's a <a> pos relative
10199 //this.focusEl.alignTo(el, "tl-tl");
10201 // this.focusEl.focus();
10203 // this.focusEl.focus.defer(1, this.focusEl);
10208 * Scrolls the specified cell into view
10209 * @param {Number} row The row index
10210 * @param {Number} col The column index
10211 * @param {Boolean} hscroll false to disable horizontal scrolling
10213 ensureVisible : function(row, col, hscroll)
10215 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10216 //return null; //disable for testing.
10217 if(typeof row != "number"){
10218 row = row.rowIndex;
10220 if(row < 0 && row >= this.ds.getCount()){
10223 col = (col !== undefined ? col : 0);
10225 while(cm.isHidden(col)){
10229 var el = this.getCellDom(row, col);
10233 var c = this.bodyEl.dom;
10235 var ctop = parseInt(el.offsetTop, 10);
10236 var cleft = parseInt(el.offsetLeft, 10);
10237 var cbot = ctop + el.offsetHeight;
10238 var cright = cleft + el.offsetWidth;
10240 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10241 var ch = 0; //?? header is not withing the area?
10242 var stop = parseInt(c.scrollTop, 10);
10243 var sleft = parseInt(c.scrollLeft, 10);
10244 var sbot = stop + ch;
10245 var sright = sleft + c.clientWidth;
10247 Roo.log('GridView.ensureVisible:' +
10249 ' c.clientHeight:' + c.clientHeight +
10250 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10258 c.scrollTop = ctop;
10259 //Roo.log("set scrolltop to ctop DISABLE?");
10260 }else if(cbot > sbot){
10261 //Roo.log("set scrolltop to cbot-ch");
10262 c.scrollTop = cbot-ch;
10265 if(hscroll !== false){
10267 c.scrollLeft = cleft;
10268 }else if(cright > sright){
10269 c.scrollLeft = cright-c.clientWidth;
10277 insertRow : function(dm, rowIndex, isUpdate){
10280 this.fireEvent("beforerowsinserted", this, rowIndex);
10282 //var s = this.getScrollState();
10283 var row = this.renderRow(this.cm, this.store, rowIndex);
10284 // insert before rowIndex..
10285 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10289 if(row.cellObjects.length){
10290 Roo.each(row.cellObjects, function(r){
10291 _this.renderCellObject(r);
10296 this.fireEvent("rowsinserted", this, rowIndex);
10297 //this.syncRowHeights(firstRow, lastRow);
10298 //this.stripeRows(firstRow);
10305 getRowDom : function(rowIndex)
10307 var rows = this.el.select('tbody > tr', true).elements;
10309 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10312 getCellDom : function(rowIndex, colIndex)
10314 var row = this.getRowDom(rowIndex);
10315 if (row === false) {
10318 var cols = row.select('td', true).elements;
10319 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10323 // returns the object tree for a tr..
10326 renderRow : function(cm, ds, rowIndex)
10328 var d = ds.getAt(rowIndex);
10332 cls : 'x-row-' + rowIndex,
10336 var cellObjects = [];
10338 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10339 var config = cm.config[i];
10341 var renderer = cm.getRenderer(i);
10345 if(typeof(renderer) !== 'undefined'){
10346 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10348 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10349 // and are rendered into the cells after the row is rendered - using the id for the element.
10351 if(typeof(value) === 'object'){
10361 rowIndex : rowIndex,
10366 this.fireEvent('rowclass', this, rowcfg);
10370 // this might end up displaying HTML?
10371 // this is too messy... - better to only do it on columsn you know are going to be too long
10372 //tooltip : (typeof(value) === 'object') ? '' : value,
10373 cls : rowcfg.rowClass + ' x-col-' + i,
10375 html: (typeof(value) === 'object') ? '' : value
10382 if(typeof(config.colspan) != 'undefined'){
10383 td.colspan = config.colspan;
10388 if(typeof(config.align) != 'undefined' && config.align.length){
10389 td.style += ' text-align:' + config.align + ';';
10391 if(typeof(config.valign) != 'undefined' && config.valign.length){
10392 td.style += ' vertical-align:' + config.valign + ';';
10395 if(typeof(config.width) != 'undefined'){
10396 td.style += ' width:' + config.width + 'px;';
10400 if(typeof(config.cursor) != 'undefined'){
10401 td.style += ' cursor:' + config.cursor + ';';
10404 if(typeof(config.cls) != 'undefined'){
10405 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10407 if (this.responsive) {
10408 ['xs','sm','md','lg'].map(function(size){
10410 if(typeof(config[size]) == 'undefined'){
10416 if (!config[size]) { // 0 = hidden
10417 // BS 4 '0' is treated as hide that column and below.
10418 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10422 td.cls += ' col-' + size + '-' + config[size] + (
10423 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10433 row.cellObjects = cellObjects;
10441 onBeforeLoad : function()
10443 this.el.unmask(); // if needed.
10450 this.el.select('tbody', true).first().dom.innerHTML = '';
10453 * Show or hide a row.
10454 * @param {Number} rowIndex to show or hide
10455 * @param {Boolean} state hide
10457 setRowVisibility : function(rowIndex, state)
10459 var bt = this.bodyEl.dom;
10461 var rows = this.el.select('tbody > tr', true).elements;
10463 if(typeof(rows[rowIndex]) == 'undefined'){
10466 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10471 getSelectionModel : function(){
10472 if(!this.selModel){
10473 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10475 return this.selModel;
10478 * Render the Roo.bootstrap object from renderder
10480 renderCellObject : function(r)
10484 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10486 var t = r.cfg.render(r.container);
10489 Roo.each(r.cfg.cn, function(c){
10491 container: t.getChildContainer(),
10494 _this.renderCellObject(child);
10499 * get the Row Index from a dom element.
10500 * @param {Roo.Element} row The row to look for
10501 * @returns {Number} the row
10503 getRowIndex : function(row)
10507 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10518 * get the header TH element for columnIndex
10519 * @param {Number} columnIndex
10520 * @returns {Roo.Element}
10522 getHeaderIndex: function(colIndex)
10524 var cols = this.headEl.select('th', true).elements;
10525 return cols[colIndex];
10528 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10529 * @param {domElement} cell to look for
10530 * @returns {Number} the column
10532 getCellIndex : function(cell)
10534 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10536 return parseInt(id[1], 10);
10541 * Returns the grid's underlying element = used by panel.Grid
10542 * @return {Element} The element
10544 getGridEl : function(){
10548 * Forces a resize - used by panel.Grid
10549 * @return {Element} The element
10551 autoSize : function()
10553 if(this.disableAutoSize) {
10556 //var ctr = Roo.get(this.container.dom.parentElement);
10557 var ctr = Roo.get(this.el.dom);
10559 var thd = this.getGridEl().select('thead',true).first();
10560 var tbd = this.getGridEl().select('tbody', true).first();
10561 var tfd = this.getGridEl().select('tfoot', true).first();
10563 var cw = ctr.getWidth();
10564 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10568 tbd.setWidth(ctr.getWidth());
10569 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10570 // this needs fixing for various usage - currently only hydra job advers I think..
10572 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10574 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10577 cw = Math.max(cw, this.totalWidth);
10578 this.getGridEl().select('tbody tr',true).setWidth(cw);
10581 // resize 'expandable coloumn?
10583 return; // we doe not have a view in this design..
10586 onBodyScroll: function()
10588 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10590 this.headEl.setStyle({
10591 'position' : 'relative',
10592 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10598 var scrollHeight = this.bodyEl.dom.scrollHeight;
10600 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10602 var height = this.bodyEl.getHeight();
10604 if(scrollHeight - height == scrollTop) {
10606 var total = this.ds.getTotalCount();
10608 if(this.footer.cursor + this.footer.pageSize < total){
10610 this.footer.ds.load({
10612 start : this.footer.cursor + this.footer.pageSize,
10613 limit : this.footer.pageSize
10622 onColumnSplitterMoved : function(i, diff)
10624 this.userResized = true;
10626 var cm = this.colModel;
10628 var w = this.getHeaderIndex(i).getWidth() + diff;
10631 cm.setColumnWidth(i, w, true);
10633 //var cid = cm.getColumnId(i); << not used in this version?
10634 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10636 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10637 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10638 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10640 //this.updateSplitters();
10641 //this.layout(); << ??
10642 this.fireEvent("columnresize", i, w);
10644 onHeaderChange : function()
10646 var header = this.renderHeader();
10647 var table = this.el.select('table', true).first();
10649 this.headEl.remove();
10650 this.headEl = table.createChild(header, this.bodyEl, false);
10652 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10653 e.on('click', this.sort, this);
10656 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10657 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10662 onHiddenChange : function(colModel, colIndex, hidden)
10665 this.cm.setHidden()
10666 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10667 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10669 this.CSS.updateRule(thSelector, "display", "");
10670 this.CSS.updateRule(tdSelector, "display", "");
10673 this.CSS.updateRule(thSelector, "display", "none");
10674 this.CSS.updateRule(tdSelector, "display", "none");
10677 // onload calls initCSS()
10678 this.onHeaderChange();
10682 setColumnWidth: function(col_index, width)
10684 // width = "md-2 xs-2..."
10685 if(!this.colModel.config[col_index]) {
10689 var w = width.split(" ");
10691 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10693 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10696 for(var j = 0; j < w.length; j++) {
10702 var size_cls = w[j].split("-");
10704 if(!Number.isInteger(size_cls[1] * 1)) {
10708 if(!this.colModel.config[col_index][size_cls[0]]) {
10712 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10716 h_row[0].classList.replace(
10717 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10718 "col-"+size_cls[0]+"-"+size_cls[1]
10721 for(var i = 0; i < rows.length; i++) {
10723 var size_cls = w[j].split("-");
10725 if(!Number.isInteger(size_cls[1] * 1)) {
10729 if(!this.colModel.config[col_index][size_cls[0]]) {
10733 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10737 rows[i].classList.replace(
10738 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10739 "col-"+size_cls[0]+"-"+size_cls[1]
10743 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10748 // currently only used to find the split on drag..
10749 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10754 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10755 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10764 * @class Roo.bootstrap.TableCell
10765 * @extends Roo.bootstrap.Component
10766 * @children Roo.bootstrap.Component
10767 * @parent Roo.bootstrap.TableRow
10768 * Bootstrap TableCell class
10770 * @cfg {String} html cell contain text
10771 * @cfg {String} cls cell class
10772 * @cfg {String} tag cell tag (td|th) default td
10773 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10774 * @cfg {String} align Aligns the content in a cell
10775 * @cfg {String} axis Categorizes cells
10776 * @cfg {String} bgcolor Specifies the background color of a cell
10777 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10778 * @cfg {Number} colspan Specifies the number of columns a cell should span
10779 * @cfg {String} headers Specifies one or more header cells a cell is related to
10780 * @cfg {Number} height Sets the height of a cell
10781 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10782 * @cfg {Number} rowspan Sets the number of rows a cell should span
10783 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10784 * @cfg {String} valign Vertical aligns the content in a cell
10785 * @cfg {Number} width Specifies the width of a cell
10788 * Create a new TableCell
10789 * @param {Object} config The config object
10792 Roo.bootstrap.TableCell = function(config){
10793 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10796 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10816 getAutoCreate : function(){
10817 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10824 cfg.tag = this.tag;
10837 cfg.align=this.align
10842 if (this.bgcolor) {
10843 cfg.bgcolor=this.bgcolor
10845 if (this.charoff) {
10846 cfg.charoff=this.charoff
10848 if (this.colspan) {
10849 cfg.colspan=this.colspan
10851 if (this.headers) {
10852 cfg.headers=this.headers
10855 cfg.height=this.height
10858 cfg.nowrap=this.nowrap
10860 if (this.rowspan) {
10861 cfg.rowspan=this.rowspan
10864 cfg.scope=this.scope
10867 cfg.valign=this.valign
10870 cfg.width=this.width
10889 * @class Roo.bootstrap.TableRow
10890 * @extends Roo.bootstrap.Component
10891 * @children Roo.bootstrap.TableCell
10892 * @parent Roo.bootstrap.TableBody
10893 * Bootstrap TableRow class
10894 * @cfg {String} cls row class
10895 * @cfg {String} align Aligns the content in a table row
10896 * @cfg {String} bgcolor Specifies a background color for a table row
10897 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10898 * @cfg {String} valign Vertical aligns the content in a table row
10901 * Create a new TableRow
10902 * @param {Object} config The config object
10905 Roo.bootstrap.TableRow = function(config){
10906 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10909 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10917 getAutoCreate : function(){
10918 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10925 cfg.cls = this.cls;
10928 cfg.align = this.align;
10931 cfg.bgcolor = this.bgcolor;
10934 cfg.charoff = this.charoff;
10937 cfg.valign = this.valign;
10955 * @class Roo.bootstrap.TableBody
10956 * @extends Roo.bootstrap.Component
10957 * @children Roo.bootstrap.TableRow
10958 * @parent Roo.bootstrap.Table
10959 * Bootstrap TableBody class
10960 * @cfg {String} cls element class
10961 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10962 * @cfg {String} align Aligns the content inside the element
10963 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10964 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10967 * Create a new TableBody
10968 * @param {Object} config The config object
10971 Roo.bootstrap.TableBody = function(config){
10972 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10975 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10983 getAutoCreate : function(){
10984 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10994 cfg.tag = this.tag;
10998 cfg.align = this.align;
11001 cfg.charoff = this.charoff;
11004 cfg.valign = this.valign;
11011 // initEvents : function()
11014 // if(!this.store){
11018 // this.store = Roo.factory(this.store, Roo.data);
11019 // this.store.on('load', this.onLoad, this);
11021 // this.store.load();
11025 // onLoad: function ()
11027 // this.fireEvent('load', this);
11037 * Ext JS Library 1.1.1
11038 * Copyright(c) 2006-2007, Ext JS, LLC.
11040 * Originally Released Under LGPL - original licence link has changed is not relivant.
11043 * <script type="text/javascript">
11046 // as we use this in bootstrap.
11047 Roo.namespace('Roo.form');
11049 * @class Roo.form.Action
11050 * Internal Class used to handle form actions
11052 * @param {Roo.form.BasicForm} el The form element or its id
11053 * @param {Object} config Configuration options
11058 // define the action interface
11059 Roo.form.Action = function(form, options){
11061 this.options = options || {};
11064 * Client Validation Failed
11067 Roo.form.Action.CLIENT_INVALID = 'client';
11069 * Server Validation Failed
11072 Roo.form.Action.SERVER_INVALID = 'server';
11074 * Connect to Server Failed
11077 Roo.form.Action.CONNECT_FAILURE = 'connect';
11079 * Reading Data from Server Failed
11082 Roo.form.Action.LOAD_FAILURE = 'load';
11084 Roo.form.Action.prototype = {
11086 failureType : undefined,
11087 response : undefined,
11088 result : undefined,
11090 // interface method
11091 run : function(options){
11095 // interface method
11096 success : function(response){
11100 // interface method
11101 handleResponse : function(response){
11105 // default connection failure
11106 failure : function(response){
11108 this.response = response;
11109 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11110 this.form.afterAction(this, false);
11113 processResponse : function(response){
11114 this.response = response;
11115 if(!response.responseText){
11118 this.result = this.handleResponse(response);
11119 return this.result;
11122 // utility functions used internally
11123 getUrl : function(appendParams){
11124 var url = this.options.url || this.form.url || this.form.el.dom.action;
11126 var p = this.getParams();
11128 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11134 getMethod : function(){
11135 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11138 getParams : function(){
11139 var bp = this.form.baseParams;
11140 var p = this.options.params;
11142 if(typeof p == "object"){
11143 p = Roo.urlEncode(Roo.applyIf(p, bp));
11144 }else if(typeof p == 'string' && bp){
11145 p += '&' + Roo.urlEncode(bp);
11148 p = Roo.urlEncode(bp);
11153 createCallback : function(){
11155 success: this.success,
11156 failure: this.failure,
11158 timeout: (this.form.timeout*1000),
11159 upload: this.form.fileUpload ? this.success : undefined
11164 Roo.form.Action.Submit = function(form, options){
11165 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11168 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11171 haveProgress : false,
11172 uploadComplete : false,
11174 // uploadProgress indicator.
11175 uploadProgress : function()
11177 if (!this.form.progressUrl) {
11181 if (!this.haveProgress) {
11182 Roo.MessageBox.progress("Uploading", "Uploading");
11184 if (this.uploadComplete) {
11185 Roo.MessageBox.hide();
11189 this.haveProgress = true;
11191 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11193 var c = new Roo.data.Connection();
11195 url : this.form.progressUrl,
11200 success : function(req){
11201 //console.log(data);
11205 rdata = Roo.decode(req.responseText)
11207 Roo.log("Invalid data from server..");
11211 if (!rdata || !rdata.success) {
11213 Roo.MessageBox.alert(Roo.encode(rdata));
11216 var data = rdata.data;
11218 if (this.uploadComplete) {
11219 Roo.MessageBox.hide();
11224 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11225 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11228 this.uploadProgress.defer(2000,this);
11231 failure: function(data) {
11232 Roo.log('progress url failed ');
11243 // run get Values on the form, so it syncs any secondary forms.
11244 this.form.getValues();
11246 var o = this.options;
11247 var method = this.getMethod();
11248 var isPost = method == 'POST';
11249 if(o.clientValidation === false || this.form.isValid()){
11251 if (this.form.progressUrl) {
11252 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11253 (new Date() * 1) + '' + Math.random());
11258 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11259 form:this.form.el.dom,
11260 url:this.getUrl(!isPost),
11262 params:isPost ? this.getParams() : null,
11263 isUpload: this.form.fileUpload,
11264 formData : this.form.formData
11267 this.uploadProgress();
11269 }else if (o.clientValidation !== false){ // client validation failed
11270 this.failureType = Roo.form.Action.CLIENT_INVALID;
11271 this.form.afterAction(this, false);
11275 success : function(response)
11277 this.uploadComplete= true;
11278 if (this.haveProgress) {
11279 Roo.MessageBox.hide();
11283 var result = this.processResponse(response);
11284 if(result === true || result.success){
11285 this.form.afterAction(this, true);
11289 this.form.markInvalid(result.errors);
11290 this.failureType = Roo.form.Action.SERVER_INVALID;
11292 this.form.afterAction(this, false);
11294 failure : function(response)
11296 this.uploadComplete= true;
11297 if (this.haveProgress) {
11298 Roo.MessageBox.hide();
11301 this.response = response;
11302 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11303 this.form.afterAction(this, false);
11306 handleResponse : function(response){
11307 if(this.form.errorReader){
11308 var rs = this.form.errorReader.read(response);
11311 for(var i = 0, len = rs.records.length; i < len; i++) {
11312 var r = rs.records[i];
11313 errors[i] = r.data;
11316 if(errors.length < 1){
11320 success : rs.success,
11326 var rt = response.responseText;
11327 if (rt.match(/^\<!--\[CDATA\[/)) {
11328 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11329 rt = rt.replace(/\]\]--\>$/,'');
11332 ret = Roo.decode(rt);
11336 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11346 Roo.form.Action.Load = function(form, options){
11347 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11348 this.reader = this.form.reader;
11351 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11356 Roo.Ajax.request(Roo.apply(
11357 this.createCallback(), {
11358 method:this.getMethod(),
11359 url:this.getUrl(false),
11360 params:this.getParams()
11364 success : function(response){
11366 var result = this.processResponse(response);
11367 if(result === true || !result.success || !result.data){
11368 this.failureType = Roo.form.Action.LOAD_FAILURE;
11369 this.form.afterAction(this, false);
11372 this.form.clearInvalid();
11373 this.form.setValues(result.data);
11374 this.form.afterAction(this, true);
11377 handleResponse : function(response){
11378 if(this.form.reader){
11379 var rs = this.form.reader.read(response);
11380 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11382 success : rs.success,
11386 return Roo.decode(response.responseText);
11390 Roo.form.Action.ACTION_TYPES = {
11391 'load' : Roo.form.Action.Load,
11392 'submit' : Roo.form.Action.Submit
11401 * @class Roo.bootstrap.form.Form
11402 * @extends Roo.bootstrap.Component
11403 * @children Roo.bootstrap.Component
11404 * Bootstrap Form class
11405 * @cfg {String} method GET | POST (default POST)
11406 * @cfg {String} labelAlign top | left (default top)
11407 * @cfg {String} align left | right - for navbars
11408 * @cfg {Boolean} loadMask load mask when submit (default true)
11412 * Create a new Form
11413 * @param {Object} config The config object
11417 Roo.bootstrap.form.Form = function(config){
11419 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11421 Roo.bootstrap.form.Form.popover.apply();
11425 * @event clientvalidation
11426 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11427 * @param {Form} this
11428 * @param {Boolean} valid true if the form has passed client-side validation
11430 clientvalidation: true,
11432 * @event beforeaction
11433 * Fires before any action is performed. Return false to cancel the action.
11434 * @param {Form} this
11435 * @param {Action} action The action to be performed
11437 beforeaction: true,
11439 * @event actionfailed
11440 * Fires when an action fails.
11441 * @param {Form} this
11442 * @param {Action} action The action that failed
11444 actionfailed : true,
11446 * @event actioncomplete
11447 * Fires when an action is completed.
11448 * @param {Form} this
11449 * @param {Action} action The action that completed
11451 actioncomplete : true
11455 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11458 * @cfg {String} method
11459 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11463 * @cfg {String} url
11464 * The URL to use for form actions if one isn't supplied in the action options.
11467 * @cfg {Boolean} fileUpload
11468 * Set to true if this form is a file upload.
11472 * @cfg {Object} baseParams
11473 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11477 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11481 * @cfg {Sting} align (left|right) for navbar forms
11486 activeAction : null,
11489 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11490 * element by passing it or its id or mask the form itself by passing in true.
11493 waitMsgTarget : false,
11498 * @cfg {Boolean} errorMask (true|false) default false
11503 * @cfg {Number} maskOffset Default 100
11508 * @cfg {Boolean} maskBody
11512 getAutoCreate : function(){
11516 method : this.method || 'POST',
11517 id : this.id || Roo.id(),
11520 if (this.parent().xtype.match(/^Nav/)) {
11521 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11525 if (this.labelAlign == 'left' ) {
11526 cfg.cls += ' form-horizontal';
11532 initEvents : function()
11534 this.el.on('submit', this.onSubmit, this);
11535 // this was added as random key presses on the form where triggering form submit.
11536 this.el.on('keypress', function(e) {
11537 if (e.getCharCode() != 13) {
11540 // we might need to allow it for textareas.. and some other items.
11541 // check e.getTarget().
11543 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11547 Roo.log("keypress blocked");
11549 e.preventDefault();
11555 onSubmit : function(e){
11560 * Returns true if client-side validation on the form is successful.
11563 isValid : function(){
11564 var items = this.getItems();
11566 var target = false;
11568 items.each(function(f){
11574 Roo.log('invalid field: ' + f.name);
11578 if(!target && f.el.isVisible(true)){
11584 if(this.errorMask && !valid){
11585 Roo.bootstrap.form.Form.popover.mask(this, target);
11592 * Returns true if any fields in this form have changed since their original load.
11595 isDirty : function(){
11597 var items = this.getItems();
11598 items.each(function(f){
11608 * Performs a predefined action (submit or load) or custom actions you define on this form.
11609 * @param {String} actionName The name of the action type
11610 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11611 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11612 * accept other config options):
11614 Property Type Description
11615 ---------------- --------------- ----------------------------------------------------------------------------------
11616 url String The url for the action (defaults to the form's url)
11617 method String The form method to use (defaults to the form's method, or POST if not defined)
11618 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11619 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11620 validate the form on the client (defaults to false)
11622 * @return {BasicForm} this
11624 doAction : function(action, options){
11625 if(typeof action == 'string'){
11626 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11628 if(this.fireEvent('beforeaction', this, action) !== false){
11629 this.beforeAction(action);
11630 action.run.defer(100, action);
11636 beforeAction : function(action){
11637 var o = action.options;
11642 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11644 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11647 // not really supported yet.. ??
11649 //if(this.waitMsgTarget === true){
11650 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11651 //}else if(this.waitMsgTarget){
11652 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11653 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11655 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11661 afterAction : function(action, success){
11662 this.activeAction = null;
11663 var o = action.options;
11668 Roo.get(document.body).unmask();
11674 //if(this.waitMsgTarget === true){
11675 // this.el.unmask();
11676 //}else if(this.waitMsgTarget){
11677 // this.waitMsgTarget.unmask();
11679 // Roo.MessageBox.updateProgress(1);
11680 // Roo.MessageBox.hide();
11687 Roo.callback(o.success, o.scope, [this, action]);
11688 this.fireEvent('actioncomplete', this, action);
11692 // failure condition..
11693 // we have a scenario where updates need confirming.
11694 // eg. if a locking scenario exists..
11695 // we look for { errors : { needs_confirm : true }} in the response.
11697 (typeof(action.result) != 'undefined') &&
11698 (typeof(action.result.errors) != 'undefined') &&
11699 (typeof(action.result.errors.needs_confirm) != 'undefined')
11702 Roo.log("not supported yet");
11705 Roo.MessageBox.confirm(
11706 "Change requires confirmation",
11707 action.result.errorMsg,
11712 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11722 Roo.callback(o.failure, o.scope, [this, action]);
11723 // show an error message if no failed handler is set..
11724 if (!this.hasListener('actionfailed')) {
11725 Roo.log("need to add dialog support");
11727 Roo.MessageBox.alert("Error",
11728 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11729 action.result.errorMsg :
11730 "Saving Failed, please check your entries or try again"
11735 this.fireEvent('actionfailed', this, action);
11740 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11741 * @param {String} id The value to search for
11744 findField : function(id){
11745 var items = this.getItems();
11746 var field = items.get(id);
11748 items.each(function(f){
11749 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11756 return field || null;
11759 * Mark fields in this form invalid in bulk.
11760 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11761 * @return {BasicForm} this
11763 markInvalid : function(errors){
11764 if(errors instanceof Array){
11765 for(var i = 0, len = errors.length; i < len; i++){
11766 var fieldError = errors[i];
11767 var f = this.findField(fieldError.id);
11769 f.markInvalid(fieldError.msg);
11775 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11776 field.markInvalid(errors[id]);
11780 //Roo.each(this.childForms || [], function (f) {
11781 // f.markInvalid(errors);
11788 * Set values for fields in this form in bulk.
11789 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11790 * @return {BasicForm} this
11792 setValues : function(values){
11793 if(values instanceof Array){ // array of objects
11794 for(var i = 0, len = values.length; i < len; i++){
11796 var f = this.findField(v.id);
11798 f.setValue(v.value);
11799 if(this.trackResetOnLoad){
11800 f.originalValue = f.getValue();
11804 }else{ // object hash
11807 if(typeof values[id] != 'function' && (field = this.findField(id))){
11809 if (field.setFromData &&
11810 field.valueField &&
11811 field.displayField &&
11812 // combos' with local stores can
11813 // be queried via setValue()
11814 // to set their value..
11815 (field.store && !field.store.isLocal)
11819 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11820 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11821 field.setFromData(sd);
11823 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11825 field.setFromData(values);
11828 field.setValue(values[id]);
11832 if(this.trackResetOnLoad){
11833 field.originalValue = field.getValue();
11839 //Roo.each(this.childForms || [], function (f) {
11840 // f.setValues(values);
11847 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11848 * they are returned as an array.
11849 * @param {Boolean} asString
11852 getValues : function(asString){
11853 //if (this.childForms) {
11854 // copy values from the child forms
11855 // Roo.each(this.childForms, function (f) {
11856 // this.setValues(f.getValues());
11862 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11863 if(asString === true){
11866 return Roo.urlDecode(fs);
11870 * Returns the fields in this form as an object with key/value pairs.
11871 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11874 getFieldValues : function(with_hidden)
11876 var items = this.getItems();
11878 items.each(function(f){
11880 if (!f.getName()) {
11884 var v = f.getValue();
11886 if (f.inputType =='radio') {
11887 if (typeof(ret[f.getName()]) == 'undefined') {
11888 ret[f.getName()] = ''; // empty..
11891 if (!f.el.dom.checked) {
11895 v = f.el.dom.value;
11899 if(f.xtype == 'MoneyField'){
11900 ret[f.currencyName] = f.getCurrency();
11903 // not sure if this supported any more..
11904 if ((typeof(v) == 'object') && f.getRawValue) {
11905 v = f.getRawValue() ; // dates..
11907 // combo boxes where name != hiddenName...
11908 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11909 ret[f.name] = f.getRawValue();
11911 ret[f.getName()] = v;
11918 * Clears all invalid messages in this form.
11919 * @return {BasicForm} this
11921 clearInvalid : function(){
11922 var items = this.getItems();
11924 items.each(function(f){
11932 * Resets this form.
11933 * @return {BasicForm} this
11935 reset : function(){
11936 var items = this.getItems();
11937 items.each(function(f){
11941 Roo.each(this.childForms || [], function (f) {
11949 getItems : function()
11951 var r=new Roo.util.MixedCollection(false, function(o){
11952 return o.id || (o.id = Roo.id());
11954 var iter = function(el) {
11961 Roo.each(el.items,function(e) {
11970 hideFields : function(items)
11972 Roo.each(items, function(i){
11974 var f = this.findField(i);
11985 showFields : function(items)
11987 Roo.each(items, function(i){
11989 var f = this.findField(i);
12002 Roo.apply(Roo.bootstrap.form.Form, {
12018 intervalID : false,
12024 if(this.isApplied){
12029 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12030 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12031 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12032 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12035 this.maskEl.top.enableDisplayMode("block");
12036 this.maskEl.left.enableDisplayMode("block");
12037 this.maskEl.bottom.enableDisplayMode("block");
12038 this.maskEl.right.enableDisplayMode("block");
12040 this.toolTip = new Roo.bootstrap.Tooltip({
12041 cls : 'roo-form-error-popover',
12043 'left' : ['r-l', [-2,0], 'right'],
12044 'right' : ['l-r', [2,0], 'left'],
12045 'bottom' : ['tl-bl', [0,2], 'top'],
12046 'top' : [ 'bl-tl', [0,-2], 'bottom']
12050 this.toolTip.render(Roo.get(document.body));
12052 this.toolTip.el.enableDisplayMode("block");
12054 Roo.get(document.body).on('click', function(){
12058 Roo.get(document.body).on('touchstart', function(){
12062 this.isApplied = true
12065 mask : function(form, target)
12069 this.target = target;
12071 if(!this.form.errorMask || !target.el){
12075 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12077 Roo.log(scrollable);
12079 var ot = this.target.el.calcOffsetsTo(scrollable);
12081 var scrollTo = ot[1] - this.form.maskOffset;
12083 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12085 scrollable.scrollTo('top', scrollTo);
12087 var box = this.target.el.getBox();
12089 var zIndex = Roo.bootstrap.Modal.zIndex++;
12092 this.maskEl.top.setStyle('position', 'absolute');
12093 this.maskEl.top.setStyle('z-index', zIndex);
12094 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12095 this.maskEl.top.setLeft(0);
12096 this.maskEl.top.setTop(0);
12097 this.maskEl.top.show();
12099 this.maskEl.left.setStyle('position', 'absolute');
12100 this.maskEl.left.setStyle('z-index', zIndex);
12101 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12102 this.maskEl.left.setLeft(0);
12103 this.maskEl.left.setTop(box.y - this.padding);
12104 this.maskEl.left.show();
12106 this.maskEl.bottom.setStyle('position', 'absolute');
12107 this.maskEl.bottom.setStyle('z-index', zIndex);
12108 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12109 this.maskEl.bottom.setLeft(0);
12110 this.maskEl.bottom.setTop(box.bottom + this.padding);
12111 this.maskEl.bottom.show();
12113 this.maskEl.right.setStyle('position', 'absolute');
12114 this.maskEl.right.setStyle('z-index', zIndex);
12115 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12116 this.maskEl.right.setLeft(box.right + this.padding);
12117 this.maskEl.right.setTop(box.y - this.padding);
12118 this.maskEl.right.show();
12120 this.toolTip.bindEl = this.target.el;
12122 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12124 var tip = this.target.blankText;
12126 if(this.target.getValue() !== '' ) {
12128 if (this.target.invalidText.length) {
12129 tip = this.target.invalidText;
12130 } else if (this.target.regexText.length){
12131 tip = this.target.regexText;
12135 this.toolTip.show(tip);
12137 this.intervalID = window.setInterval(function() {
12138 Roo.bootstrap.form.Form.popover.unmask();
12141 window.onwheel = function(){ return false;};
12143 (function(){ this.isMasked = true; }).defer(500, this);
12147 unmask : function()
12149 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12153 this.maskEl.top.setStyle('position', 'absolute');
12154 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12155 this.maskEl.top.hide();
12157 this.maskEl.left.setStyle('position', 'absolute');
12158 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12159 this.maskEl.left.hide();
12161 this.maskEl.bottom.setStyle('position', 'absolute');
12162 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12163 this.maskEl.bottom.hide();
12165 this.maskEl.right.setStyle('position', 'absolute');
12166 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12167 this.maskEl.right.hide();
12169 this.toolTip.hide();
12171 this.toolTip.el.hide();
12173 window.onwheel = function(){ return true;};
12175 if(this.intervalID){
12176 window.clearInterval(this.intervalID);
12177 this.intervalID = false;
12180 this.isMasked = false;
12190 * Ext JS Library 1.1.1
12191 * Copyright(c) 2006-2007, Ext JS, LLC.
12193 * Originally Released Under LGPL - original licence link has changed is not relivant.
12196 * <script type="text/javascript">
12199 * @class Roo.form.VTypes
12200 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12203 Roo.form.VTypes = function(){
12204 // closure these in so they are only created once.
12205 var alpha = /^[a-zA-Z_]+$/;
12206 var alphanum = /^[a-zA-Z0-9_]+$/;
12207 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12208 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12210 // All these messages and functions are configurable
12213 * The function used to validate email addresses
12214 * @param {String} value The email address
12216 email : function(v){
12217 return email.test(v);
12220 * The error text to display when the email validation function returns false
12223 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12225 * The keystroke filter mask to be applied on email input
12228 emailMask : /[a-z0-9_\.\-@]/i,
12231 * The function used to validate URLs
12232 * @param {String} value The URL
12235 return url.test(v);
12238 * The error text to display when the url validation function returns false
12241 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12244 * The function used to validate alpha values
12245 * @param {String} value The value
12247 alpha : function(v){
12248 return alpha.test(v);
12251 * The error text to display when the alpha validation function returns false
12254 alphaText : 'This field should only contain letters and _',
12256 * The keystroke filter mask to be applied on alpha input
12259 alphaMask : /[a-z_]/i,
12262 * The function used to validate alphanumeric values
12263 * @param {String} value The value
12265 alphanum : function(v){
12266 return alphanum.test(v);
12269 * The error text to display when the alphanumeric validation function returns false
12272 alphanumText : 'This field should only contain letters, numbers and _',
12274 * The keystroke filter mask to be applied on alphanumeric input
12277 alphanumMask : /[a-z0-9_]/i
12287 * @class Roo.bootstrap.form.Input
12288 * @extends Roo.bootstrap.Component
12289 * Bootstrap Input class
12290 * @cfg {Boolean} disabled is it disabled
12291 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12292 * @cfg {String} name name of the input
12293 * @cfg {string} fieldLabel - the label associated
12294 * @cfg {string} placeholder - placeholder to put in text.
12295 * @cfg {string} before - input group add on before
12296 * @cfg {string} after - input group add on after
12297 * @cfg {string} size - (lg|sm) or leave empty..
12298 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12299 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12300 * @cfg {Number} md colspan out of 12 for computer-sized screens
12301 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12302 * @cfg {string} value default value of the input
12303 * @cfg {Number} labelWidth set the width of label
12304 * @cfg {Number} labellg set the width of label (1-12)
12305 * @cfg {Number} labelmd set the width of label (1-12)
12306 * @cfg {Number} labelsm set the width of label (1-12)
12307 * @cfg {Number} labelxs set the width of label (1-12)
12308 * @cfg {String} labelAlign (top|left)
12309 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12310 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12311 * @cfg {String} indicatorpos (left|right) default left
12312 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12313 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12314 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12315 * @cfg {Roo.bootstrap.Button} before Button to show before
12316 * @cfg {Roo.bootstrap.Button} afterButton to show before
12317 * @cfg {String} align (left|center|right) Default left
12318 * @cfg {Boolean} forceFeedback (true|false) Default false
12321 * Create a new Input
12322 * @param {Object} config The config object
12325 Roo.bootstrap.form.Input = function(config){
12327 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12332 * Fires when this field receives input focus.
12333 * @param {Roo.form.Field} this
12338 * Fires when this field loses input focus.
12339 * @param {Roo.form.Field} this
12343 * @event specialkey
12344 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12345 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12346 * @param {Roo.form.Field} this
12347 * @param {Roo.EventObject} e The event object
12352 * Fires just before the field blurs if the field value has changed.
12353 * @param {Roo.form.Field} this
12354 * @param {Mixed} newValue The new value
12355 * @param {Mixed} oldValue The original value
12360 * Fires after the field has been marked as invalid.
12361 * @param {Roo.form.Field} this
12362 * @param {String} msg The validation message
12367 * Fires after the field has been validated with no errors.
12368 * @param {Roo.form.Field} this
12373 * Fires after the key up
12374 * @param {Roo.form.Field} this
12375 * @param {Roo.EventObject} e The event Object
12380 * Fires after the user pastes into input
12381 * @param {Roo.form.Field} this
12382 * @param {Roo.EventObject} e The event Object
12388 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12390 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12391 automatic validation (defaults to "keyup").
12393 validationEvent : "keyup",
12395 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12397 validateOnBlur : true,
12399 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12401 validationDelay : 250,
12403 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12405 focusClass : "x-form-focus", // not needed???
12409 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12411 invalidClass : "has-warning",
12414 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12416 validClass : "has-success",
12419 * @cfg {Boolean} hasFeedback (true|false) default true
12421 hasFeedback : true,
12424 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12426 invalidFeedbackClass : "glyphicon-warning-sign",
12429 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12431 validFeedbackClass : "glyphicon-ok",
12434 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12436 selectOnFocus : false,
12439 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12443 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12448 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12450 disableKeyFilter : false,
12453 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12457 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12461 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12463 blankText : "Please complete this mandatory field",
12466 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12470 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12472 maxLength : Number.MAX_VALUE,
12474 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12476 minLengthText : "The minimum length for this field is {0}",
12478 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12480 maxLengthText : "The maximum length for this field is {0}",
12484 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12485 * If available, this function will be called only after the basic validators all return true, and will be passed the
12486 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12490 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12491 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12492 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12496 * @cfg {String} regexText -- Depricated - use Invalid Text
12501 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12507 autocomplete: false,
12511 inputType : 'text',
12514 placeholder: false,
12519 preventMark: false,
12520 isFormField : true,
12523 labelAlign : false,
12526 formatedValue : false,
12527 forceFeedback : false,
12529 indicatorpos : 'left',
12539 parentLabelAlign : function()
12542 while (parent.parent()) {
12543 parent = parent.parent();
12544 if (typeof(parent.labelAlign) !='undefined') {
12545 return parent.labelAlign;
12552 getAutoCreate : function()
12559 if(this.inputType != 'hidden'){
12560 cfg.cls = 'form-group' //input-group
12566 type : this.inputType,
12567 value : this.value,
12568 cls : 'form-control',
12569 placeholder : this.placeholder || '',
12570 autocomplete : this.autocomplete || 'new-password'
12572 if (this.inputType == 'file') {
12573 input.style = 'overflow:hidden'; // why not in CSS?
12576 if(this.capture.length){
12577 input.capture = this.capture;
12580 if(this.accept.length){
12581 input.accept = this.accept + "/*";
12585 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12588 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12589 input.maxLength = this.maxLength;
12592 if (this.disabled) {
12593 input.disabled=true;
12596 if (this.readOnly) {
12597 input.readonly=true;
12601 input.name = this.name;
12605 input.cls += ' input-' + this.size;
12609 ['xs','sm','md','lg'].map(function(size){
12610 if (settings[size]) {
12611 cfg.cls += ' col-' + size + '-' + settings[size];
12615 var inputblock = input;
12619 cls: 'glyphicon form-control-feedback'
12622 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12625 cls : 'has-feedback',
12633 if (this.before || this.after) {
12636 cls : 'input-group',
12640 if (this.before && typeof(this.before) == 'string') {
12642 inputblock.cn.push({
12644 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12648 if (this.before && typeof(this.before) == 'object') {
12649 this.before = Roo.factory(this.before);
12651 inputblock.cn.push({
12653 cls : 'roo-input-before input-group-prepend input-group-' +
12654 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12658 inputblock.cn.push(input);
12660 if (this.after && typeof(this.after) == 'string') {
12661 inputblock.cn.push({
12663 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12667 if (this.after && typeof(this.after) == 'object') {
12668 this.after = Roo.factory(this.after);
12670 inputblock.cn.push({
12672 cls : 'roo-input-after input-group-append input-group-' +
12673 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12677 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12678 inputblock.cls += ' has-feedback';
12679 inputblock.cn.push(feedback);
12685 cfg = this.getAutoCreateLabel( cfg, inputblock );
12690 if (this.parentType === 'Navbar' && this.parent().bar) {
12691 cfg.cls += ' navbar-form';
12694 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12695 // on BS4 we do this only if not form
12696 cfg.cls += ' navbar-form';
12704 * autocreate the label - also used by textara... ?? and others?
12706 getAutoCreateLabel : function( cfg, inputblock )
12708 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12712 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12713 tooltip : 'This field is required'
12715 if (this.allowBlank ) {
12716 indicator.style = this.allowBlank ? ' display:none' : '';
12718 if (align ==='left' && this.fieldLabel.length) {
12720 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12727 cls : 'control-label col-form-label',
12728 html : this.fieldLabel
12739 var labelCfg = cfg.cn[1];
12740 var contentCfg = cfg.cn[2];
12742 if(this.indicatorpos == 'right'){
12747 cls : 'control-label col-form-label',
12751 html : this.fieldLabel
12765 labelCfg = cfg.cn[0];
12766 contentCfg = cfg.cn[1];
12770 if(this.labelWidth > 12){
12771 labelCfg.style = "width: " + this.labelWidth + 'px';
12774 if(this.labelWidth < 13 && this.labelmd == 0){
12775 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12778 if(this.labellg > 0){
12779 labelCfg.cls += ' col-lg-' + this.labellg;
12780 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12783 if(this.labelmd > 0){
12784 labelCfg.cls += ' col-md-' + this.labelmd;
12785 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12788 if(this.labelsm > 0){
12789 labelCfg.cls += ' col-sm-' + this.labelsm;
12790 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12793 if(this.labelxs > 0){
12794 labelCfg.cls += ' col-xs-' + this.labelxs;
12795 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12799 } else if ( this.fieldLabel.length) {
12806 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12807 tooltip : 'This field is required',
12808 style : this.allowBlank ? ' display:none' : ''
12812 //cls : 'input-group-addon',
12813 html : this.fieldLabel
12821 if(this.indicatorpos == 'right'){
12826 //cls : 'input-group-addon',
12827 html : this.fieldLabel
12832 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12833 tooltip : 'This field is required',
12834 style : this.allowBlank ? ' display:none' : ''
12858 * return the real input element.
12860 inputEl: function ()
12862 return this.el.select('input.form-control',true).first();
12865 tooltipEl : function()
12867 return this.inputEl();
12870 indicatorEl : function()
12872 if (Roo.bootstrap.version == 4) {
12873 return false; // not enabled in v4 yet.
12876 var indicator = this.el.select('i.roo-required-indicator',true).first();
12886 setDisabled : function(v)
12888 var i = this.inputEl().dom;
12890 i.removeAttribute('disabled');
12894 i.setAttribute('disabled','true');
12896 initEvents : function()
12899 this.inputEl().on("keydown" , this.fireKey, this);
12900 this.inputEl().on("focus", this.onFocus, this);
12901 this.inputEl().on("blur", this.onBlur, this);
12903 this.inputEl().relayEvent('keyup', this);
12904 this.inputEl().relayEvent('paste', this);
12906 this.indicator = this.indicatorEl();
12908 if(this.indicator){
12909 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12912 // reference to original value for reset
12913 this.originalValue = this.getValue();
12914 //Roo.form.TextField.superclass.initEvents.call(this);
12915 if(this.validationEvent == 'keyup'){
12916 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12917 this.inputEl().on('keyup', this.filterValidation, this);
12919 else if(this.validationEvent !== false){
12920 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12923 if(this.selectOnFocus){
12924 this.on("focus", this.preFocus, this);
12927 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12928 this.inputEl().on("keypress", this.filterKeys, this);
12930 this.inputEl().relayEvent('keypress', this);
12933 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12934 this.el.on("click", this.autoSize, this);
12937 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12938 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12941 if (typeof(this.before) == 'object') {
12942 this.before.render(this.el.select('.roo-input-before',true).first());
12944 if (typeof(this.after) == 'object') {
12945 this.after.render(this.el.select('.roo-input-after',true).first());
12948 this.inputEl().on('change', this.onChange, this);
12951 filterValidation : function(e){
12952 if(!e.isNavKeyPress()){
12953 this.validationTask.delay(this.validationDelay);
12957 * Validates the field value
12958 * @return {Boolean} True if the value is valid, else false
12960 validate : function(){
12961 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12962 if(this.disabled || this.validateValue(this.getRawValue())){
12967 this.markInvalid();
12973 * Validates a value according to the field's validation rules and marks the field as invalid
12974 * if the validation fails
12975 * @param {Mixed} value The value to validate
12976 * @return {Boolean} True if the value is valid, else false
12978 validateValue : function(value)
12980 if(this.getVisibilityEl().hasClass('hidden')){
12984 if(value.length < 1) { // if it's blank
12985 if(this.allowBlank){
12991 if(value.length < this.minLength){
12994 if(value.length > this.maxLength){
12998 var vt = Roo.form.VTypes;
12999 if(!vt[this.vtype](value, this)){
13003 if(typeof this.validator == "function"){
13004 var msg = this.validator(value);
13005 if (typeof(msg) == 'string') {
13006 this.invalidText = msg;
13013 if(this.regex && !this.regex.test(value)){
13021 fireKey : function(e){
13022 //Roo.log('field ' + e.getKey());
13023 if(e.isNavKeyPress()){
13024 this.fireEvent("specialkey", this, e);
13027 focus : function (selectText){
13029 this.inputEl().focus();
13030 if(selectText === true){
13031 this.inputEl().dom.select();
13037 onFocus : function(){
13038 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13039 // this.el.addClass(this.focusClass);
13041 if(!this.hasFocus){
13042 this.hasFocus = true;
13043 this.startValue = this.getValue();
13044 this.fireEvent("focus", this);
13048 beforeBlur : Roo.emptyFn,
13052 onBlur : function(){
13054 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13055 //this.el.removeClass(this.focusClass);
13057 this.hasFocus = false;
13058 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13061 var v = this.getValue();
13062 if(String(v) !== String(this.startValue)){
13063 this.fireEvent('change', this, v, this.startValue);
13065 this.fireEvent("blur", this);
13068 onChange : function(e)
13070 var v = this.getValue();
13071 if(String(v) !== String(this.startValue)){
13072 this.fireEvent('change', this, v, this.startValue);
13078 * Resets the current field value to the originally loaded value and clears any validation messages
13080 reset : function(){
13081 this.setValue(this.originalValue);
13085 * Returns the name of the field
13086 * @return {Mixed} name The name field
13088 getName: function(){
13092 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13093 * @return {Mixed} value The field value
13095 getValue : function(){
13096 var v = this.inputEl().getValue();
13100 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13101 * @return {Mixed} value The field value
13103 getRawValue : function(){
13104 var v = this.inputEl().getValue();
13110 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13111 * @param {Mixed} value The value to set
13113 setRawValue : function(v){
13114 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13117 selectText : function(start, end){
13118 var v = this.getRawValue();
13120 start = start === undefined ? 0 : start;
13121 end = end === undefined ? v.length : end;
13122 var d = this.inputEl().dom;
13123 if(d.setSelectionRange){
13124 d.setSelectionRange(start, end);
13125 }else if(d.createTextRange){
13126 var range = d.createTextRange();
13127 range.moveStart("character", start);
13128 range.moveEnd("character", v.length-end);
13135 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13136 * @param {Mixed} value The value to set
13138 setValue : function(v){
13141 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13147 processValue : function(value){
13148 if(this.stripCharsRe){
13149 var newValue = value.replace(this.stripCharsRe, '');
13150 if(newValue !== value){
13151 this.setRawValue(newValue);
13158 preFocus : function(){
13160 if(this.selectOnFocus){
13161 this.inputEl().dom.select();
13164 filterKeys : function(e){
13165 var k = e.getKey();
13166 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13169 var c = e.getCharCode(), cc = String.fromCharCode(c);
13170 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13173 if(!this.maskRe.test(cc)){
13178 * Clear any invalid styles/messages for this field
13180 clearInvalid : function(){
13182 if(!this.el || this.preventMark){ // not rendered
13187 this.el.removeClass([this.invalidClass, 'is-invalid']);
13189 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13191 var feedback = this.el.select('.form-control-feedback', true).first();
13194 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13199 if(this.indicator){
13200 this.indicator.removeClass('visible');
13201 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13204 this.fireEvent('valid', this);
13208 * Mark this field as valid
13210 markValid : function()
13212 if(!this.el || this.preventMark){ // not rendered...
13216 this.el.removeClass([this.invalidClass, this.validClass]);
13217 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13219 var feedback = this.el.select('.form-control-feedback', true).first();
13222 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13225 if(this.indicator){
13226 this.indicator.removeClass('visible');
13227 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13235 if(this.allowBlank && !this.getRawValue().length){
13238 if (Roo.bootstrap.version == 3) {
13239 this.el.addClass(this.validClass);
13241 this.inputEl().addClass('is-valid');
13244 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13246 var feedback = this.el.select('.form-control-feedback', true).first();
13249 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13250 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13255 this.fireEvent('valid', this);
13259 * Mark this field as invalid
13260 * @param {String} msg The validation message
13262 markInvalid : function(msg)
13264 if(!this.el || this.preventMark){ // not rendered
13268 this.el.removeClass([this.invalidClass, this.validClass]);
13269 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13271 var feedback = this.el.select('.form-control-feedback', true).first();
13274 this.el.select('.form-control-feedback', true).first().removeClass(
13275 [this.invalidFeedbackClass, this.validFeedbackClass]);
13282 if(this.allowBlank && !this.getRawValue().length){
13286 if(this.indicator){
13287 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13288 this.indicator.addClass('visible');
13290 if (Roo.bootstrap.version == 3) {
13291 this.el.addClass(this.invalidClass);
13293 this.inputEl().addClass('is-invalid');
13298 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13300 var feedback = this.el.select('.form-control-feedback', true).first();
13303 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13305 if(this.getValue().length || this.forceFeedback){
13306 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13313 this.fireEvent('invalid', this, msg);
13316 SafariOnKeyDown : function(event)
13318 // this is a workaround for a password hang bug on chrome/ webkit.
13319 if (this.inputEl().dom.type != 'password') {
13323 var isSelectAll = false;
13325 if(this.inputEl().dom.selectionEnd > 0){
13326 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13328 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13329 event.preventDefault();
13334 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13336 event.preventDefault();
13337 // this is very hacky as keydown always get's upper case.
13339 var cc = String.fromCharCode(event.getCharCode());
13340 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13344 adjustWidth : function(tag, w){
13345 tag = tag.toLowerCase();
13346 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13347 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13348 if(tag == 'input'){
13351 if(tag == 'textarea'){
13354 }else if(Roo.isOpera){
13355 if(tag == 'input'){
13358 if(tag == 'textarea'){
13366 setFieldLabel : function(v)
13368 if(!this.rendered){
13372 if(this.indicatorEl()){
13373 var ar = this.el.select('label > span',true);
13375 if (ar.elements.length) {
13376 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13377 this.fieldLabel = v;
13381 var br = this.el.select('label',true);
13383 if(br.elements.length) {
13384 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13385 this.fieldLabel = v;
13389 Roo.log('Cannot Found any of label > span || label in input');
13393 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13394 this.fieldLabel = v;
13409 * @class Roo.bootstrap.form.TextArea
13410 * @extends Roo.bootstrap.form.Input
13411 * Bootstrap TextArea class
13412 * @cfg {Number} cols Specifies the visible width of a text area
13413 * @cfg {Number} rows Specifies the visible number of lines in a text area
13414 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13415 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13416 * @cfg {string} html text
13419 * Create a new TextArea
13420 * @param {Object} config The config object
13423 Roo.bootstrap.form.TextArea = function(config){
13424 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13428 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13438 getAutoCreate : function(){
13440 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13446 if(this.inputType != 'hidden'){
13447 cfg.cls = 'form-group' //input-group
13455 value : this.value || '',
13456 html: this.html || '',
13457 cls : 'form-control',
13458 placeholder : this.placeholder || ''
13462 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13463 input.maxLength = this.maxLength;
13467 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13471 input.cols = this.cols;
13474 if (this.readOnly) {
13475 input.readonly = true;
13479 input.name = this.name;
13483 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13487 ['xs','sm','md','lg'].map(function(size){
13488 if (settings[size]) {
13489 cfg.cls += ' col-' + size + '-' + settings[size];
13493 var inputblock = input;
13495 if(this.hasFeedback && !this.allowBlank){
13499 cls: 'glyphicon form-control-feedback'
13503 cls : 'has-feedback',
13512 if (this.before || this.after) {
13515 cls : 'input-group',
13519 inputblock.cn.push({
13521 cls : 'input-group-addon',
13526 inputblock.cn.push(input);
13528 if(this.hasFeedback && !this.allowBlank){
13529 inputblock.cls += ' has-feedback';
13530 inputblock.cn.push(feedback);
13534 inputblock.cn.push({
13536 cls : 'input-group-addon',
13544 cfg = this.getAutoCreateLabel( cfg, inputblock );
13548 if (this.disabled) {
13549 input.disabled=true;
13556 * return the real textarea element.
13558 inputEl: function ()
13560 return this.el.select('textarea.form-control',true).first();
13564 * Clear any invalid styles/messages for this field
13566 clearInvalid : function()
13569 if(!this.el || this.preventMark){ // not rendered
13573 var label = this.el.select('label', true).first();
13574 //var icon = this.el.select('i.fa-star', true).first();
13576 //if(label && icon){
13579 this.el.removeClass( this.validClass);
13580 this.inputEl().removeClass('is-invalid');
13582 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13584 var feedback = this.el.select('.form-control-feedback', true).first();
13587 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13592 this.fireEvent('valid', this);
13596 * Mark this field as valid
13598 markValid : function()
13600 if(!this.el || this.preventMark){ // not rendered
13604 this.el.removeClass([this.invalidClass, this.validClass]);
13605 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13607 var feedback = this.el.select('.form-control-feedback', true).first();
13610 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13613 if(this.disabled || this.allowBlank){
13617 var label = this.el.select('label', true).first();
13618 var icon = this.el.select('i.fa-star', true).first();
13620 //if(label && icon){
13623 if (Roo.bootstrap.version == 3) {
13624 this.el.addClass(this.validClass);
13626 this.inputEl().addClass('is-valid');
13630 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13632 var feedback = this.el.select('.form-control-feedback', true).first();
13635 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13636 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13641 this.fireEvent('valid', this);
13645 * Mark this field as invalid
13646 * @param {String} msg The validation message
13648 markInvalid : function(msg)
13650 if(!this.el || this.preventMark){ // not rendered
13654 this.el.removeClass([this.invalidClass, this.validClass]);
13655 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13657 var feedback = this.el.select('.form-control-feedback', true).first();
13660 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13667 var label = this.el.select('label', true).first();
13668 //var icon = this.el.select('i.fa-star', true).first();
13670 //if(!this.getValue().length && label && !icon){
13671 /* this.el.createChild({
13673 cls : 'text-danger fa fa-lg fa-star',
13674 tooltip : 'This field is required',
13675 style : 'margin-right:5px;'
13680 if (Roo.bootstrap.version == 3) {
13681 this.el.addClass(this.invalidClass);
13683 this.inputEl().addClass('is-invalid');
13686 // fixme ... this may be depricated need to test..
13687 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13689 var feedback = this.el.select('.form-control-feedback', true).first();
13692 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13694 if(this.getValue().length || this.forceFeedback){
13695 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13702 this.fireEvent('invalid', this, msg);
13710 * trigger field - base class for combo..
13715 * @class Roo.bootstrap.form.TriggerField
13716 * @extends Roo.bootstrap.form.Input
13717 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13718 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13719 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13720 * for which you can provide a custom implementation. For example:
13722 var trigger = new Roo.bootstrap.form.TriggerField();
13723 trigger.onTriggerClick = myTriggerFn;
13724 trigger.applyTo('my-field');
13727 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13728 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13729 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13730 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13731 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13734 * Create a new TriggerField.
13735 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13736 * to the base TextField)
13738 Roo.bootstrap.form.TriggerField = function(config){
13739 this.mimicing = false;
13740 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13743 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13745 * @cfg {String} triggerClass A CSS class to apply to the trigger
13748 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13753 * @cfg {Boolean} removable (true|false) special filter default false
13757 /** @cfg {Boolean} grow @hide */
13758 /** @cfg {Number} growMin @hide */
13759 /** @cfg {Number} growMax @hide */
13765 autoSize: Roo.emptyFn,
13769 deferHeight : true,
13772 actionMode : 'wrap',
13777 getAutoCreate : function(){
13779 var align = this.labelAlign || this.parentLabelAlign();
13784 cls: 'form-group' //input-group
13791 type : this.inputType,
13792 cls : 'form-control',
13793 autocomplete: 'new-password',
13794 placeholder : this.placeholder || ''
13798 input.name = this.name;
13801 input.cls += ' input-' + this.size;
13804 if (this.disabled) {
13805 input.disabled=true;
13808 var inputblock = input;
13810 if(this.hasFeedback && !this.allowBlank){
13814 cls: 'glyphicon form-control-feedback'
13817 if(this.removable && !this.editable ){
13819 cls : 'has-feedback',
13825 cls : 'roo-combo-removable-btn close'
13832 cls : 'has-feedback',
13841 if(this.removable && !this.editable ){
13843 cls : 'roo-removable',
13849 cls : 'roo-combo-removable-btn close'
13856 if (this.before || this.after) {
13859 cls : 'input-group',
13863 inputblock.cn.push({
13865 cls : 'input-group-addon input-group-prepend input-group-text',
13870 inputblock.cn.push(input);
13872 if(this.hasFeedback && !this.allowBlank){
13873 inputblock.cls += ' has-feedback';
13874 inputblock.cn.push(feedback);
13878 inputblock.cn.push({
13880 cls : 'input-group-addon input-group-append input-group-text',
13889 var ibwrap = inputblock;
13894 cls: 'roo-select2-choices',
13898 cls: 'roo-select2-search-field',
13910 cls: 'roo-select2-container input-group',
13915 cls: 'form-hidden-field'
13921 if(!this.multiple && this.showToggleBtn){
13927 if (this.caret != false) {
13930 cls: 'fa fa-' + this.caret
13937 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13939 Roo.bootstrap.version == 3 ? caret : '',
13942 cls: 'combobox-clear',
13956 combobox.cls += ' roo-select2-container-multi';
13960 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13961 tooltip : 'This field is required'
13964 if (this.allowBlank) {
13967 style : 'display:none'
13973 if (align ==='left' && this.fieldLabel.length) {
13975 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13982 cls : 'control-label',
13983 html : this.fieldLabel
13995 var labelCfg = cfg.cn[1];
13996 var contentCfg = cfg.cn[2];
13998 if(this.indicatorpos == 'right'){
14003 cls : 'control-label',
14007 html : this.fieldLabel
14021 labelCfg = cfg.cn[0];
14022 contentCfg = cfg.cn[1];
14025 if(this.labelWidth > 12){
14026 labelCfg.style = "width: " + this.labelWidth + 'px';
14029 if(this.labelWidth < 13 && this.labelmd == 0){
14030 this.labelmd = this.labelWidth;
14033 if(this.labellg > 0){
14034 labelCfg.cls += ' col-lg-' + this.labellg;
14035 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14038 if(this.labelmd > 0){
14039 labelCfg.cls += ' col-md-' + this.labelmd;
14040 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14043 if(this.labelsm > 0){
14044 labelCfg.cls += ' col-sm-' + this.labelsm;
14045 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14048 if(this.labelxs > 0){
14049 labelCfg.cls += ' col-xs-' + this.labelxs;
14050 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14053 } else if ( this.fieldLabel.length) {
14054 // Roo.log(" label");
14059 //cls : 'input-group-addon',
14060 html : this.fieldLabel
14068 if(this.indicatorpos == 'right'){
14076 html : this.fieldLabel
14090 // Roo.log(" no label && no align");
14097 ['xs','sm','md','lg'].map(function(size){
14098 if (settings[size]) {
14099 cfg.cls += ' col-' + size + '-' + settings[size];
14110 onResize : function(w, h){
14111 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14112 // if(typeof w == 'number'){
14113 // var x = w - this.trigger.getWidth();
14114 // this.inputEl().setWidth(this.adjustWidth('input', x));
14115 // this.trigger.setStyle('left', x+'px');
14120 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14123 getResizeEl : function(){
14124 return this.inputEl();
14128 getPositionEl : function(){
14129 return this.inputEl();
14133 alignErrorIcon : function(){
14134 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14138 initEvents : function(){
14142 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14143 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14144 if(!this.multiple && this.showToggleBtn){
14145 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14146 if(this.hideTrigger){
14147 this.trigger.setDisplayed(false);
14149 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14153 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14156 if(this.removable && !this.editable && !this.tickable){
14157 var close = this.closeTriggerEl();
14160 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14161 close.on('click', this.removeBtnClick, this, close);
14165 //this.trigger.addClassOnOver('x-form-trigger-over');
14166 //this.trigger.addClassOnClick('x-form-trigger-click');
14169 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14173 closeTriggerEl : function()
14175 var close = this.el.select('.roo-combo-removable-btn', true).first();
14176 return close ? close : false;
14179 removeBtnClick : function(e, h, el)
14181 e.preventDefault();
14183 if(this.fireEvent("remove", this) !== false){
14185 this.fireEvent("afterremove", this)
14189 createList : function()
14191 this.list = Roo.get(document.body).createChild({
14192 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14193 cls: 'typeahead typeahead-long dropdown-menu shadow',
14194 style: 'display:none'
14197 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14202 initTrigger : function(){
14207 onDestroy : function(){
14209 this.trigger.removeAllListeners();
14210 // this.trigger.remove();
14213 // this.wrap.remove();
14215 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14219 onFocus : function(){
14220 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14222 if(!this.mimicing){
14223 this.wrap.addClass('x-trigger-wrap-focus');
14224 this.mimicing = true;
14225 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14226 if(this.monitorTab){
14227 this.el.on("keydown", this.checkTab, this);
14234 checkTab : function(e){
14235 if(e.getKey() == e.TAB){
14236 this.triggerBlur();
14241 onBlur : function(){
14246 mimicBlur : function(e, t){
14248 if(!this.wrap.contains(t) && this.validateBlur()){
14249 this.triggerBlur();
14255 triggerBlur : function(){
14256 this.mimicing = false;
14257 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14258 if(this.monitorTab){
14259 this.el.un("keydown", this.checkTab, this);
14261 //this.wrap.removeClass('x-trigger-wrap-focus');
14262 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14266 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14267 validateBlur : function(e, t){
14272 onDisable : function(){
14273 this.inputEl().dom.disabled = true;
14274 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14276 // this.wrap.addClass('x-item-disabled');
14281 onEnable : function(){
14282 this.inputEl().dom.disabled = false;
14283 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14285 // this.el.removeClass('x-item-disabled');
14290 onShow : function(){
14291 var ae = this.getActionEl();
14294 ae.dom.style.display = '';
14295 ae.dom.style.visibility = 'visible';
14301 onHide : function(){
14302 var ae = this.getActionEl();
14303 ae.dom.style.display = 'none';
14307 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14308 * by an implementing function.
14310 * @param {EventObject} e
14312 onTriggerClick : Roo.emptyFn
14320 * @class Roo.bootstrap.form.CardUploader
14321 * @extends Roo.bootstrap.Button
14322 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14323 * @cfg {Number} errorTimeout default 3000
14324 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14325 * @cfg {Array} html The button text.
14329 * Create a new CardUploader
14330 * @param {Object} config The config object
14333 Roo.bootstrap.form.CardUploader = function(config){
14337 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14340 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14348 * When a image is clicked on - and needs to display a slideshow or similar..
14349 * @param {Roo.bootstrap.Card} this
14350 * @param {Object} The image information data
14356 * When a the download link is clicked
14357 * @param {Roo.bootstrap.Card} this
14358 * @param {Object} The image information data contains
14365 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14368 errorTimeout : 3000,
14372 fileCollection : false,
14375 getAutoCreate : function()
14379 cls :'form-group' ,
14384 //cls : 'input-group-addon',
14385 html : this.fieldLabel
14393 value : this.value,
14394 cls : 'd-none form-control'
14399 multiple : 'multiple',
14401 cls : 'd-none roo-card-upload-selector'
14405 cls : 'roo-card-uploader-button-container w-100 mb-2'
14408 cls : 'card-columns roo-card-uploader-container'
14418 getChildContainer : function() /// what children are added to.
14420 return this.containerEl;
14423 getButtonContainer : function() /// what children are added to.
14425 return this.el.select(".roo-card-uploader-button-container").first();
14428 initEvents : function()
14431 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14435 xns: Roo.bootstrap,
14438 container_method : 'getButtonContainer' ,
14439 html : this.html, // fix changable?
14442 'click' : function(btn, e) {
14451 this.urlAPI = (window.createObjectURL && window) ||
14452 (window.URL && URL.revokeObjectURL && URL) ||
14453 (window.webkitURL && webkitURL);
14458 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14460 this.selectorEl.on('change', this.onFileSelected, this);
14463 this.images.forEach(function(img) {
14466 this.images = false;
14468 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14474 onClick : function(e)
14476 e.preventDefault();
14478 this.selectorEl.dom.click();
14482 onFileSelected : function(e)
14484 e.preventDefault();
14486 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14490 Roo.each(this.selectorEl.dom.files, function(file){
14491 this.addFile(file);
14500 addFile : function(file)
14503 if(typeof(file) === 'string'){
14504 throw "Add file by name?"; // should not happen
14508 if(!file || !this.urlAPI){
14518 var url = _this.urlAPI.createObjectURL( file);
14521 id : Roo.bootstrap.form.CardUploader.ID--,
14522 is_uploaded : false,
14526 mimetype : file.type,
14534 * addCard - add an Attachment to the uploader
14535 * @param data - the data about the image to upload
14539 title : "Title of file",
14540 is_uploaded : false,
14541 src : "http://.....",
14542 srcfile : { the File upload object },
14543 mimetype : file.type,
14546 .. any other data...
14552 addCard : function (data)
14554 // hidden input element?
14555 // if the file is not an image...
14556 //then we need to use something other that and header_image
14561 xns : Roo.bootstrap,
14562 xtype : 'CardFooter',
14565 xns : Roo.bootstrap,
14571 xns : Roo.bootstrap,
14573 html : String.format("<small>{0}</small>", data.title),
14574 cls : 'col-10 text-left',
14579 click : function() {
14581 t.fireEvent( "download", t, data );
14587 xns : Roo.bootstrap,
14589 style: 'max-height: 28px; ',
14595 click : function() {
14596 t.removeCard(data.id)
14608 var cn = this.addxtype(
14611 xns : Roo.bootstrap,
14614 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14615 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14616 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14621 initEvents : function() {
14622 Roo.bootstrap.Card.prototype.initEvents.call(this);
14624 this.imgEl = this.el.select('.card-img-top').first();
14626 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14627 this.imgEl.set({ 'pointer' : 'cursor' });
14630 this.getCardFooter().addClass('p-1');
14637 // dont' really need ot update items.
14638 // this.items.push(cn);
14639 this.fileCollection.add(cn);
14641 if (!data.srcfile) {
14642 this.updateInput();
14647 var reader = new FileReader();
14648 reader.addEventListener("load", function() {
14649 data.srcdata = reader.result;
14652 reader.readAsDataURL(data.srcfile);
14657 removeCard : function(id)
14660 var card = this.fileCollection.get(id);
14661 card.data.is_deleted = 1;
14662 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14663 //this.fileCollection.remove(card);
14664 //this.items = this.items.filter(function(e) { return e != card });
14665 // dont' really need ot update items.
14666 card.el.dom.parentNode.removeChild(card.el.dom);
14667 this.updateInput();
14673 this.fileCollection.each(function(card) {
14674 if (card.el.dom && card.el.dom.parentNode) {
14675 card.el.dom.parentNode.removeChild(card.el.dom);
14678 this.fileCollection.clear();
14679 this.updateInput();
14682 updateInput : function()
14685 this.fileCollection.each(function(e) {
14689 this.inputEl().dom.value = JSON.stringify(data);
14699 Roo.bootstrap.form.CardUploader.ID = -1;/*
14701 * Ext JS Library 1.1.1
14702 * Copyright(c) 2006-2007, Ext JS, LLC.
14704 * Originally Released Under LGPL - original licence link has changed is not relivant.
14707 * <script type="text/javascript">
14712 * @class Roo.data.SortTypes
14714 * Defines the default sorting (casting?) comparison functions used when sorting data.
14716 Roo.data.SortTypes = {
14718 * Default sort that does nothing
14719 * @param {Mixed} s The value being converted
14720 * @return {Mixed} The comparison value
14722 none : function(s){
14727 * The regular expression used to strip tags
14731 stripTagsRE : /<\/?[^>]+>/gi,
14734 * Strips all HTML tags to sort on text only
14735 * @param {Mixed} s The value being converted
14736 * @return {String} The comparison value
14738 asText : function(s){
14739 return String(s).replace(this.stripTagsRE, "");
14743 * Strips all HTML tags to sort on text only - Case insensitive
14744 * @param {Mixed} s The value being converted
14745 * @return {String} The comparison value
14747 asUCText : function(s){
14748 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14752 * Case insensitive string
14753 * @param {Mixed} s The value being converted
14754 * @return {String} The comparison value
14756 asUCString : function(s) {
14757 return String(s).toUpperCase();
14762 * @param {Mixed} s The value being converted
14763 * @return {Number} The comparison value
14765 asDate : function(s) {
14769 if(s instanceof Date){
14770 return s.getTime();
14772 return Date.parse(String(s));
14777 * @param {Mixed} s The value being converted
14778 * @return {Float} The comparison value
14780 asFloat : function(s) {
14781 var val = parseFloat(String(s).replace(/,/g, ""));
14790 * @param {Mixed} s The value being converted
14791 * @return {Number} The comparison value
14793 asInt : function(s) {
14794 var val = parseInt(String(s).replace(/,/g, ""));
14802 * Ext JS Library 1.1.1
14803 * Copyright(c) 2006-2007, Ext JS, LLC.
14805 * Originally Released Under LGPL - original licence link has changed is not relivant.
14808 * <script type="text/javascript">
14812 * @class Roo.data.Record
14813 * Instances of this class encapsulate both record <em>definition</em> information, and record
14814 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14815 * to access Records cached in an {@link Roo.data.Store} object.<br>
14817 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14818 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14821 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14823 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14824 * {@link #create}. The parameters are the same.
14825 * @param {Array} data An associative Array of data values keyed by the field name.
14826 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14827 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14828 * not specified an integer id is generated.
14830 Roo.data.Record = function(data, id){
14831 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14836 * Generate a constructor for a specific record layout.
14837 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14838 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14839 * Each field definition object may contain the following properties: <ul>
14840 * <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,
14841 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14842 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14843 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14844 * is being used, then this is a string containing the javascript expression to reference the data relative to
14845 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14846 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14847 * this may be omitted.</p></li>
14848 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14849 * <ul><li>auto (Default, implies no conversion)</li>
14854 * <li>date</li></ul></p></li>
14855 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14856 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14857 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14858 * by the Reader into an object that will be stored in the Record. It is passed the
14859 * following parameters:<ul>
14860 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14862 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14864 * <br>usage:<br><pre><code>
14865 var TopicRecord = Roo.data.Record.create(
14866 {name: 'title', mapping: 'topic_title'},
14867 {name: 'author', mapping: 'username'},
14868 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14869 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14870 {name: 'lastPoster', mapping: 'user2'},
14871 {name: 'excerpt', mapping: 'post_text'}
14874 var myNewRecord = new TopicRecord({
14875 title: 'Do my job please',
14878 lastPost: new Date(),
14879 lastPoster: 'Animal',
14880 excerpt: 'No way dude!'
14882 myStore.add(myNewRecord);
14887 Roo.data.Record.create = function(o){
14888 var f = function(){
14889 f.superclass.constructor.apply(this, arguments);
14891 Roo.extend(f, Roo.data.Record);
14892 var p = f.prototype;
14893 p.fields = new Roo.util.MixedCollection(false, function(field){
14896 for(var i = 0, len = o.length; i < len; i++){
14897 p.fields.add(new Roo.data.Field(o[i]));
14899 f.getField = function(name){
14900 return p.fields.get(name);
14905 Roo.data.Record.AUTO_ID = 1000;
14906 Roo.data.Record.EDIT = 'edit';
14907 Roo.data.Record.REJECT = 'reject';
14908 Roo.data.Record.COMMIT = 'commit';
14910 Roo.data.Record.prototype = {
14912 * Readonly flag - true if this record has been modified.
14921 join : function(store){
14922 this.store = store;
14926 * Set the named field to the specified value.
14927 * @param {String} name The name of the field to set.
14928 * @param {Object} value The value to set the field to.
14930 set : function(name, value){
14931 if(this.data[name] == value){
14935 if(!this.modified){
14936 this.modified = {};
14938 if(typeof this.modified[name] == 'undefined'){
14939 this.modified[name] = this.data[name];
14941 this.data[name] = value;
14942 if(!this.editing && this.store){
14943 this.store.afterEdit(this);
14948 * Get the value of the named field.
14949 * @param {String} name The name of the field to get the value of.
14950 * @return {Object} The value of the field.
14952 get : function(name){
14953 return this.data[name];
14957 beginEdit : function(){
14958 this.editing = true;
14959 this.modified = {};
14963 cancelEdit : function(){
14964 this.editing = false;
14965 delete this.modified;
14969 endEdit : function(){
14970 this.editing = false;
14971 if(this.dirty && this.store){
14972 this.store.afterEdit(this);
14977 * Usually called by the {@link Roo.data.Store} which owns the Record.
14978 * Rejects all changes made to the Record since either creation, or the last commit operation.
14979 * Modified fields are reverted to their original values.
14981 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14982 * of reject operations.
14984 reject : function(){
14985 var m = this.modified;
14987 if(typeof m[n] != "function"){
14988 this.data[n] = m[n];
14991 this.dirty = false;
14992 delete this.modified;
14993 this.editing = false;
14995 this.store.afterReject(this);
15000 * Usually called by the {@link Roo.data.Store} which owns the Record.
15001 * Commits all changes made to the Record since either creation, or the last commit operation.
15003 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15004 * of commit operations.
15006 commit : function(){
15007 this.dirty = false;
15008 delete this.modified;
15009 this.editing = false;
15011 this.store.afterCommit(this);
15016 hasError : function(){
15017 return this.error != null;
15021 clearError : function(){
15026 * Creates a copy of this record.
15027 * @param {String} id (optional) A new record id if you don't want to use this record's id
15030 copy : function(newId) {
15031 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15035 * Ext JS Library 1.1.1
15036 * Copyright(c) 2006-2007, Ext JS, LLC.
15038 * Originally Released Under LGPL - original licence link has changed is not relivant.
15041 * <script type="text/javascript">
15047 * @class Roo.data.Store
15048 * @extends Roo.util.Observable
15049 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15050 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15052 * 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
15053 * has no knowledge of the format of the data returned by the Proxy.<br>
15055 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15056 * instances from the data object. These records are cached and made available through accessor functions.
15058 * Creates a new Store.
15059 * @param {Object} config A config object containing the objects needed for the Store to access data,
15060 * and read the data into Records.
15062 Roo.data.Store = function(config){
15063 this.data = new Roo.util.MixedCollection(false);
15064 this.data.getKey = function(o){
15067 this.baseParams = {};
15069 this.paramNames = {
15074 "multisort" : "_multisort"
15077 if(config && config.data){
15078 this.inlineData = config.data;
15079 delete config.data;
15082 Roo.apply(this, config);
15084 if(this.reader){ // reader passed
15085 this.reader = Roo.factory(this.reader, Roo.data);
15086 this.reader.xmodule = this.xmodule || false;
15087 if(!this.recordType){
15088 this.recordType = this.reader.recordType;
15090 if(this.reader.onMetaChange){
15091 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15095 if(this.recordType){
15096 this.fields = this.recordType.prototype.fields;
15098 this.modified = [];
15102 * @event datachanged
15103 * Fires when the data cache has changed, and a widget which is using this Store
15104 * as a Record cache should refresh its view.
15105 * @param {Store} this
15107 datachanged : true,
15109 * @event metachange
15110 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15111 * @param {Store} this
15112 * @param {Object} meta The JSON metadata
15117 * Fires when Records have been added to the Store
15118 * @param {Store} this
15119 * @param {Roo.data.Record[]} records The array of Records added
15120 * @param {Number} index The index at which the record(s) were added
15125 * Fires when a Record has been removed from the Store
15126 * @param {Store} this
15127 * @param {Roo.data.Record} record The Record that was removed
15128 * @param {Number} index The index at which the record was removed
15133 * Fires when a Record has been updated
15134 * @param {Store} this
15135 * @param {Roo.data.Record} record The Record that was updated
15136 * @param {String} operation The update operation being performed. Value may be one of:
15138 Roo.data.Record.EDIT
15139 Roo.data.Record.REJECT
15140 Roo.data.Record.COMMIT
15146 * Fires when the data cache has been cleared.
15147 * @param {Store} this
15151 * @event beforeload
15152 * Fires before a request is made for a new data object. If the beforeload handler returns false
15153 * the load action will be canceled.
15154 * @param {Store} this
15155 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15159 * @event beforeloadadd
15160 * Fires after a new set of Records has been loaded.
15161 * @param {Store} this
15162 * @param {Roo.data.Record[]} records The Records that were loaded
15163 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15165 beforeloadadd : true,
15168 * Fires after a new set of Records has been loaded, before they are added to the store.
15169 * @param {Store} this
15170 * @param {Roo.data.Record[]} records The Records that were loaded
15171 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15172 * @params {Object} return from reader
15176 * @event loadexception
15177 * Fires if an exception occurs in the Proxy during loading.
15178 * Called with the signature of the Proxy's "loadexception" event.
15179 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15182 * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15183 * @param {Object} opts - load Options
15184 * @param {Object} jsonData from your request (normally this contains the Exception)
15186 loadexception : true
15190 this.proxy = Roo.factory(this.proxy, Roo.data);
15191 this.proxy.xmodule = this.xmodule || false;
15192 this.relayEvents(this.proxy, ["loadexception"]);
15194 this.sortToggle = {};
15195 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15197 Roo.data.Store.superclass.constructor.call(this);
15199 if(this.inlineData){
15200 this.loadData(this.inlineData);
15201 delete this.inlineData;
15205 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15207 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15208 * without a remote query - used by combo/forms at present.
15212 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15215 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15218 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15219 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15222 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15223 * on any HTTP request
15226 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15229 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15233 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15234 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15236 remoteSort : false,
15239 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15240 * loaded or when a record is removed. (defaults to false).
15242 pruneModifiedRecords : false,
15245 lastOptions : null,
15248 * Add Records to the Store and fires the add event.
15249 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15251 add : function(records){
15252 records = [].concat(records);
15253 for(var i = 0, len = records.length; i < len; i++){
15254 records[i].join(this);
15256 var index = this.data.length;
15257 this.data.addAll(records);
15258 this.fireEvent("add", this, records, index);
15262 * Remove a Record from the Store and fires the remove event.
15263 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15265 remove : function(record){
15266 var index = this.data.indexOf(record);
15267 this.data.removeAt(index);
15269 if(this.pruneModifiedRecords){
15270 this.modified.remove(record);
15272 this.fireEvent("remove", this, record, index);
15276 * Remove all Records from the Store and fires the clear event.
15278 removeAll : function(){
15280 if(this.pruneModifiedRecords){
15281 this.modified = [];
15283 this.fireEvent("clear", this);
15287 * Inserts Records to the Store at the given index and fires the add event.
15288 * @param {Number} index The start index at which to insert the passed Records.
15289 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15291 insert : function(index, records){
15292 records = [].concat(records);
15293 for(var i = 0, len = records.length; i < len; i++){
15294 this.data.insert(index, records[i]);
15295 records[i].join(this);
15297 this.fireEvent("add", this, records, index);
15301 * Get the index within the cache of the passed Record.
15302 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15303 * @return {Number} The index of the passed Record. Returns -1 if not found.
15305 indexOf : function(record){
15306 return this.data.indexOf(record);
15310 * Get the index within the cache of the Record with the passed id.
15311 * @param {String} id The id of the Record to find.
15312 * @return {Number} The index of the Record. Returns -1 if not found.
15314 indexOfId : function(id){
15315 return this.data.indexOfKey(id);
15319 * Get the Record with the specified id.
15320 * @param {String} id The id of the Record to find.
15321 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15323 getById : function(id){
15324 return this.data.key(id);
15328 * Get the Record at the specified index.
15329 * @param {Number} index The index of the Record to find.
15330 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15332 getAt : function(index){
15333 return this.data.itemAt(index);
15337 * Returns a range of Records between specified indices.
15338 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15339 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15340 * @return {Roo.data.Record[]} An array of Records
15342 getRange : function(start, end){
15343 return this.data.getRange(start, end);
15347 storeOptions : function(o){
15348 o = Roo.apply({}, o);
15351 this.lastOptions = o;
15355 * Loads the Record cache from the configured Proxy using the configured Reader.
15357 * If using remote paging, then the first load call must specify the <em>start</em>
15358 * and <em>limit</em> properties in the options.params property to establish the initial
15359 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15361 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15362 * and this call will return before the new data has been loaded. Perform any post-processing
15363 * in a callback function, or in a "load" event handler.</strong>
15365 * @param {Object} options An object containing properties which control loading options:<ul>
15366 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15367 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15370 data : data, // array of key=>value data like JsonReader
15371 total : data.length,
15377 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15378 * passed the following arguments:<ul>
15379 * <li>r : Roo.data.Record[]</li>
15380 * <li>options: Options object from the load call</li>
15381 * <li>success: Boolean success indicator</li></ul></li>
15382 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15383 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15386 load : function(options){
15387 options = options || {};
15388 if(this.fireEvent("beforeload", this, options) !== false){
15389 this.storeOptions(options);
15390 var p = Roo.apply(options.params || {}, this.baseParams);
15391 // if meta was not loaded from remote source.. try requesting it.
15392 if (!this.reader.metaFromRemote) {
15393 p._requestMeta = 1;
15395 if(this.sortInfo && this.remoteSort){
15396 var pn = this.paramNames;
15397 p[pn["sort"]] = this.sortInfo.field;
15398 p[pn["dir"]] = this.sortInfo.direction;
15400 if (this.multiSort) {
15401 var pn = this.paramNames;
15402 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15405 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15410 * Reloads the Record cache from the configured Proxy using the configured Reader and
15411 * the options from the last load operation performed.
15412 * @param {Object} options (optional) An object containing properties which may override the options
15413 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15414 * the most recently used options are reused).
15416 reload : function(options){
15417 this.load(Roo.applyIf(options||{}, this.lastOptions));
15421 // Called as a callback by the Reader during a load operation.
15422 loadRecords : function(o, options, success){
15425 if(success !== false){
15426 this.fireEvent("load", this, [], options, o);
15428 if(options.callback){
15429 options.callback.call(options.scope || this, [], options, false);
15433 // if data returned failure - throw an exception.
15434 if (o.success === false) {
15435 // show a message if no listener is registered.
15436 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15437 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15439 // loadmask wil be hooked into this..
15440 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15443 var r = o.records, t = o.totalRecords || r.length;
15445 this.fireEvent("beforeloadadd", this, r, options, o);
15447 if(!options || options.add !== true){
15448 if(this.pruneModifiedRecords){
15449 this.modified = [];
15451 for(var i = 0, len = r.length; i < len; i++){
15455 this.data = this.snapshot;
15456 delete this.snapshot;
15459 this.data.addAll(r);
15460 this.totalLength = t;
15462 this.fireEvent("datachanged", this);
15464 this.totalLength = Math.max(t, this.data.length+r.length);
15468 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15470 var e = new Roo.data.Record({});
15472 e.set(this.parent.displayField, this.parent.emptyTitle);
15473 e.set(this.parent.valueField, '');
15478 this.fireEvent("load", this, r, options, o);
15479 if(options.callback){
15480 options.callback.call(options.scope || this, r, options, true);
15486 * Loads data from a passed data block. A Reader which understands the format of the data
15487 * must have been configured in the constructor.
15488 * @param {Object} data The data block from which to read the Records. The format of the data expected
15489 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15490 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15492 loadData : function(o, append){
15493 var r = this.reader.readRecords(o);
15494 this.loadRecords(r, {add: append}, true);
15498 * using 'cn' the nested child reader read the child array into it's child stores.
15499 * @param {Object} rec The record with a 'children array
15501 loadDataFromChildren : function(rec)
15503 this.loadData(this.reader.toLoadData(rec));
15508 * Gets the number of cached records.
15510 * <em>If using paging, this may not be the total size of the dataset. If the data object
15511 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15512 * the data set size</em>
15514 getCount : function(){
15515 return this.data.length || 0;
15519 * Gets the total number of records in the dataset as returned by the server.
15521 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15522 * the dataset size</em>
15524 getTotalCount : function(){
15525 return this.totalLength || 0;
15529 * Returns the sort state of the Store as an object with two properties:
15531 field {String} The name of the field by which the Records are sorted
15532 direction {String} The sort order, "ASC" or "DESC"
15535 getSortState : function(){
15536 return this.sortInfo;
15540 applySort : function(){
15541 if(this.sortInfo && !this.remoteSort){
15542 var s = this.sortInfo, f = s.field;
15543 var st = this.fields.get(f).sortType;
15544 var fn = function(r1, r2){
15545 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15546 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15548 this.data.sort(s.direction, fn);
15549 if(this.snapshot && this.snapshot != this.data){
15550 this.snapshot.sort(s.direction, fn);
15556 * Sets the default sort column and order to be used by the next load operation.
15557 * @param {String} fieldName The name of the field to sort by.
15558 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15560 setDefaultSort : function(field, dir){
15561 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15565 * Sort the Records.
15566 * If remote sorting is used, the sort is performed on the server, and the cache is
15567 * reloaded. If local sorting is used, the cache is sorted internally.
15568 * @param {String} fieldName The name of the field to sort by.
15569 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15571 sort : function(fieldName, dir){
15572 var f = this.fields.get(fieldName);
15574 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15576 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15577 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15582 this.sortToggle[f.name] = dir;
15583 this.sortInfo = {field: f.name, direction: dir};
15584 if(!this.remoteSort){
15586 this.fireEvent("datachanged", this);
15588 this.load(this.lastOptions);
15593 * Calls the specified function for each of the Records in the cache.
15594 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15595 * Returning <em>false</em> aborts and exits the iteration.
15596 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15598 each : function(fn, scope){
15599 this.data.each(fn, scope);
15603 * Gets all records modified since the last commit. Modified records are persisted across load operations
15604 * (e.g., during paging).
15605 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15607 getModifiedRecords : function(){
15608 return this.modified;
15612 createFilterFn : function(property, value, anyMatch){
15613 if(!value.exec){ // not a regex
15614 value = String(value);
15615 if(value.length == 0){
15618 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15620 return function(r){
15621 return value.test(r.data[property]);
15626 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15627 * @param {String} property A field on your records
15628 * @param {Number} start The record index to start at (defaults to 0)
15629 * @param {Number} end The last record index to include (defaults to length - 1)
15630 * @return {Number} The sum
15632 sum : function(property, start, end){
15633 var rs = this.data.items, v = 0;
15634 start = start || 0;
15635 end = (end || end === 0) ? end : rs.length-1;
15637 for(var i = start; i <= end; i++){
15638 v += (rs[i].data[property] || 0);
15644 * Filter the records by a specified property.
15645 * @param {String} field A field on your records
15646 * @param {String/RegExp} value Either a string that the field
15647 * should start with or a RegExp to test against the field
15648 * @param {Boolean} anyMatch True to match any part not just the beginning
15650 filter : function(property, value, anyMatch){
15651 var fn = this.createFilterFn(property, value, anyMatch);
15652 return fn ? this.filterBy(fn) : this.clearFilter();
15656 * Filter by a function. The specified function will be called with each
15657 * record in this data source. If the function returns true the record is included,
15658 * otherwise it is filtered.
15659 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15660 * @param {Object} scope (optional) The scope of the function (defaults to this)
15662 filterBy : function(fn, scope){
15663 this.snapshot = this.snapshot || this.data;
15664 this.data = this.queryBy(fn, scope||this);
15665 this.fireEvent("datachanged", this);
15669 * Query the records by a specified property.
15670 * @param {String} field A field on your records
15671 * @param {String/RegExp} value Either a string that the field
15672 * should start with or a RegExp to test against the field
15673 * @param {Boolean} anyMatch True to match any part not just the beginning
15674 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15676 query : function(property, value, anyMatch){
15677 var fn = this.createFilterFn(property, value, anyMatch);
15678 return fn ? this.queryBy(fn) : this.data.clone();
15682 * Query by a function. The specified function will be called with each
15683 * record in this data source. If the function returns true the record is included
15685 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15686 * @param {Object} scope (optional) The scope of the function (defaults to this)
15687 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15689 queryBy : function(fn, scope){
15690 var data = this.snapshot || this.data;
15691 return data.filterBy(fn, scope||this);
15695 * Collects unique values for a particular dataIndex from this store.
15696 * @param {String} dataIndex The property to collect
15697 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15698 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15699 * @return {Array} An array of the unique values
15701 collect : function(dataIndex, allowNull, bypassFilter){
15702 var d = (bypassFilter === true && this.snapshot) ?
15703 this.snapshot.items : this.data.items;
15704 var v, sv, r = [], l = {};
15705 for(var i = 0, len = d.length; i < len; i++){
15706 v = d[i].data[dataIndex];
15708 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15717 * Revert to a view of the Record cache with no filtering applied.
15718 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15720 clearFilter : function(suppressEvent){
15721 if(this.snapshot && this.snapshot != this.data){
15722 this.data = this.snapshot;
15723 delete this.snapshot;
15724 if(suppressEvent !== true){
15725 this.fireEvent("datachanged", this);
15731 afterEdit : function(record){
15732 if(this.modified.indexOf(record) == -1){
15733 this.modified.push(record);
15735 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15739 afterReject : function(record){
15740 this.modified.remove(record);
15741 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15745 afterCommit : function(record){
15746 this.modified.remove(record);
15747 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15751 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15752 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15754 commitChanges : function(){
15755 var m = this.modified.slice(0);
15756 this.modified = [];
15757 for(var i = 0, len = m.length; i < len; i++){
15763 * Cancel outstanding changes on all changed records.
15765 rejectChanges : function(){
15766 var m = this.modified.slice(0);
15767 this.modified = [];
15768 for(var i = 0, len = m.length; i < len; i++){
15773 onMetaChange : function(meta, rtype, o){
15774 this.recordType = rtype;
15775 this.fields = rtype.prototype.fields;
15776 delete this.snapshot;
15777 this.sortInfo = meta.sortInfo || this.sortInfo;
15778 this.modified = [];
15779 this.fireEvent('metachange', this, this.reader.meta);
15782 moveIndex : function(data, type)
15784 var index = this.indexOf(data);
15786 var newIndex = index + type;
15790 this.insert(newIndex, data);
15795 * Ext JS Library 1.1.1
15796 * Copyright(c) 2006-2007, Ext JS, LLC.
15798 * Originally Released Under LGPL - original licence link has changed is not relivant.
15801 * <script type="text/javascript">
15805 * @class Roo.data.SimpleStore
15806 * @extends Roo.data.Store
15807 * Small helper class to make creating Stores from Array data easier.
15808 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15809 * @cfg {Array} fields An array of field definition objects, or field name strings.
15810 * @cfg {Object} an existing reader (eg. copied from another store)
15811 * @cfg {Array} data The multi-dimensional array of data
15812 * @cfg {Roo.data.DataProxy} proxy [not-required]
15813 * @cfg {Roo.data.Reader} reader [not-required]
15815 * @param {Object} config
15817 Roo.data.SimpleStore = function(config)
15819 Roo.data.SimpleStore.superclass.constructor.call(this, {
15821 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15824 Roo.data.Record.create(config.fields)
15826 proxy : new Roo.data.MemoryProxy(config.data)
15830 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15832 * Ext JS Library 1.1.1
15833 * Copyright(c) 2006-2007, Ext JS, LLC.
15835 * Originally Released Under LGPL - original licence link has changed is not relivant.
15838 * <script type="text/javascript">
15843 * @extends Roo.data.Store
15844 * @class Roo.data.JsonStore
15845 * Small helper class to make creating Stores for JSON data easier. <br/>
15847 var store = new Roo.data.JsonStore({
15848 url: 'get-images.php',
15850 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15853 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15854 * JsonReader and HttpProxy (unless inline data is provided).</b>
15855 * @cfg {Array} fields An array of field definition objects, or field name strings.
15857 * @param {Object} config
15859 Roo.data.JsonStore = function(c){
15860 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15861 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15862 reader: new Roo.data.JsonReader(c, c.fields)
15865 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15867 * Ext JS Library 1.1.1
15868 * Copyright(c) 2006-2007, Ext JS, LLC.
15870 * Originally Released Under LGPL - original licence link has changed is not relivant.
15873 * <script type="text/javascript">
15877 Roo.data.Field = function(config){
15878 if(typeof config == "string"){
15879 config = {name: config};
15881 Roo.apply(this, config);
15884 this.type = "auto";
15887 var st = Roo.data.SortTypes;
15888 // named sortTypes are supported, here we look them up
15889 if(typeof this.sortType == "string"){
15890 this.sortType = st[this.sortType];
15893 // set default sortType for strings and dates
15894 if(!this.sortType){
15897 this.sortType = st.asUCString;
15900 this.sortType = st.asDate;
15903 this.sortType = st.none;
15908 var stripRe = /[\$,%]/g;
15910 // prebuilt conversion function for this field, instead of
15911 // switching every time we're reading a value
15913 var cv, dateFormat = this.dateFormat;
15918 cv = function(v){ return v; };
15921 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15925 return v !== undefined && v !== null && v !== '' ?
15926 parseInt(String(v).replace(stripRe, ""), 10) : '';
15931 return v !== undefined && v !== null && v !== '' ?
15932 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15937 cv = function(v){ return v === true || v === "true" || v == 1; };
15944 if(v instanceof Date){
15948 if(dateFormat == "timestamp"){
15949 return new Date(v*1000);
15951 return Date.parseDate(v, dateFormat);
15953 var parsed = Date.parse(v);
15954 return parsed ? new Date(parsed) : null;
15963 Roo.data.Field.prototype = {
15971 * Ext JS Library 1.1.1
15972 * Copyright(c) 2006-2007, Ext JS, LLC.
15974 * Originally Released Under LGPL - original licence link has changed is not relivant.
15977 * <script type="text/javascript">
15980 // Base class for reading structured data from a data source. This class is intended to be
15981 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15984 * @class Roo.data.DataReader
15986 * Base class for reading structured data from a data source. This class is intended to be
15987 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15990 Roo.data.DataReader = function(meta, recordType){
15994 this.recordType = recordType instanceof Array ?
15995 Roo.data.Record.create(recordType) : recordType;
15998 Roo.data.DataReader.prototype = {
16001 readerType : 'Data',
16003 * Create an empty record
16004 * @param {Object} data (optional) - overlay some values
16005 * @return {Roo.data.Record} record created.
16007 newRow : function(d) {
16009 this.recordType.prototype.fields.each(function(c) {
16011 case 'int' : da[c.name] = 0; break;
16012 case 'date' : da[c.name] = new Date(); break;
16013 case 'float' : da[c.name] = 0.0; break;
16014 case 'boolean' : da[c.name] = false; break;
16015 default : da[c.name] = ""; break;
16019 return new this.recordType(Roo.apply(da, d));
16025 * Ext JS Library 1.1.1
16026 * Copyright(c) 2006-2007, Ext JS, LLC.
16028 * Originally Released Under LGPL - original licence link has changed is not relivant.
16031 * <script type="text/javascript">
16035 * @class Roo.data.DataProxy
16036 * @extends Roo.util.Observable
16038 * This class is an abstract base class for implementations which provide retrieval of
16039 * unformatted data objects.<br>
16041 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16042 * (of the appropriate type which knows how to parse the data object) to provide a block of
16043 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16045 * Custom implementations must implement the load method as described in
16046 * {@link Roo.data.HttpProxy#load}.
16048 Roo.data.DataProxy = function(){
16051 * @event beforeload
16052 * Fires before a network request is made to retrieve a data object.
16053 * @param {Object} This DataProxy object.
16054 * @param {Object} params The params parameter to the load function.
16059 * Fires before the load method's callback is called.
16060 * @param {Object} This DataProxy object.
16061 * @param {Object} o The data object.
16062 * @param {Object} arg The callback argument object passed to the load function.
16066 * @event loadexception
16067 * Fires if an Exception occurs during data retrieval.
16068 * @param {Object} This DataProxy object.
16069 * @param {Object} o The data object.
16070 * @param {Object} arg The callback argument object passed to the load function.
16071 * @param {Object} e The Exception.
16073 loadexception : true
16075 Roo.data.DataProxy.superclass.constructor.call(this);
16078 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16081 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16085 * Ext JS Library 1.1.1
16086 * Copyright(c) 2006-2007, Ext JS, LLC.
16088 * Originally Released Under LGPL - original licence link has changed is not relivant.
16091 * <script type="text/javascript">
16094 * @class Roo.data.MemoryProxy
16095 * @extends Roo.data.DataProxy
16096 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16097 * to the Reader when its load method is called.
16099 * @param {Object} config A config object containing the objects needed for the Store to access data,
16101 Roo.data.MemoryProxy = function(config){
16103 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16104 data = config.data;
16106 Roo.data.MemoryProxy.superclass.constructor.call(this);
16110 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16113 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16116 * Load data from the requested source (in this case an in-memory
16117 * data object passed to the constructor), read the data object into
16118 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16119 * process that block using the passed callback.
16120 * @param {Object} params This parameter is not used by the MemoryProxy class.
16121 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16122 * object into a block of Roo.data.Records.
16123 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16124 * The function must be passed <ul>
16125 * <li>The Record block object</li>
16126 * <li>The "arg" argument from the load function</li>
16127 * <li>A boolean success indicator</li>
16129 * @param {Object} scope The scope in which to call the callback
16130 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16132 load : function(params, reader, callback, scope, arg){
16133 params = params || {};
16136 result = reader.readRecords(params.data ? params.data :this.data);
16138 this.fireEvent("loadexception", this, arg, null, e);
16139 callback.call(scope, null, arg, false);
16142 callback.call(scope, result, arg, true);
16146 update : function(params, records){
16151 * Ext JS Library 1.1.1
16152 * Copyright(c) 2006-2007, Ext JS, LLC.
16154 * Originally Released Under LGPL - original licence link has changed is not relivant.
16157 * <script type="text/javascript">
16160 * @class Roo.data.HttpProxy
16161 * @extends Roo.data.DataProxy
16162 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16163 * configured to reference a certain URL.<br><br>
16165 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16166 * from which the running page was served.<br><br>
16168 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16170 * Be aware that to enable the browser to parse an XML document, the server must set
16171 * the Content-Type header in the HTTP response to "text/xml".
16173 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16174 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16175 * will be used to make the request.
16177 Roo.data.HttpProxy = function(conn){
16178 Roo.data.HttpProxy.superclass.constructor.call(this);
16179 // is conn a conn config or a real conn?
16181 this.useAjax = !conn || !conn.events;
16185 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16186 // thse are take from connection...
16189 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
16192 * @cfg {Object} extraParams An object containing properties which are used as
16193 * extra parameters to each request made by this object. (defaults to undefined)
16196 * @cfg {Object} defaultHeaders An object containing request headers which are added
16197 * to each request made by this object. (defaults to undefined)
16200 * @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)
16203 * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16206 * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16212 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16216 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16217 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16218 * a finer-grained basis than the DataProxy events.
16220 getConnection : function(){
16221 return this.useAjax ? Roo.Ajax : this.conn;
16225 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16226 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16227 * process that block using the passed callback.
16228 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16229 * for the request to the remote server.
16230 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16231 * object into a block of Roo.data.Records.
16232 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16233 * The function must be passed <ul>
16234 * <li>The Record block object</li>
16235 * <li>The "arg" argument from the load function</li>
16236 * <li>A boolean success indicator</li>
16238 * @param {Object} scope The scope in which to call the callback
16239 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16241 load : function(params, reader, callback, scope, arg){
16242 if(this.fireEvent("beforeload", this, params) !== false){
16244 params : params || {},
16246 callback : callback,
16251 callback : this.loadResponse,
16255 Roo.applyIf(o, this.conn);
16256 if(this.activeRequest){
16257 Roo.Ajax.abort(this.activeRequest);
16259 this.activeRequest = Roo.Ajax.request(o);
16261 this.conn.request(o);
16264 callback.call(scope||this, null, arg, false);
16269 loadResponse : function(o, success, response){
16270 delete this.activeRequest;
16272 this.fireEvent("loadexception", this, o, response);
16273 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16278 result = o.reader.read(response);
16281 o.raw = { errorMsg : response.responseText };
16282 this.fireEvent("loadexception", this, o, response, e);
16283 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16287 this.fireEvent("load", this, o, o.request.arg);
16288 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16292 update : function(dataSet){
16297 updateResponse : function(dataSet){
16302 * Ext JS Library 1.1.1
16303 * Copyright(c) 2006-2007, Ext JS, LLC.
16305 * Originally Released Under LGPL - original licence link has changed is not relivant.
16308 * <script type="text/javascript">
16312 * @class Roo.data.ScriptTagProxy
16313 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16314 * other than the originating domain of the running page.<br><br>
16316 * <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
16317 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16319 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16320 * source code that is used as the source inside a <script> tag.<br><br>
16322 * In order for the browser to process the returned data, the server must wrap the data object
16323 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16324 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16325 * depending on whether the callback name was passed:
16328 boolean scriptTag = false;
16329 String cb = request.getParameter("callback");
16332 response.setContentType("text/javascript");
16334 response.setContentType("application/x-json");
16336 Writer out = response.getWriter();
16338 out.write(cb + "(");
16340 out.print(dataBlock.toJsonString());
16347 * @param {Object} config A configuration object.
16349 Roo.data.ScriptTagProxy = function(config){
16350 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16351 Roo.apply(this, config);
16352 this.head = document.getElementsByTagName("head")[0];
16355 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16357 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16359 * @cfg {String} url The URL from which to request the data object.
16362 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16366 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16367 * the server the name of the callback function set up by the load call to process the returned data object.
16368 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16369 * javascript output which calls this named function passing the data object as its only parameter.
16371 callbackParam : "callback",
16373 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16374 * name to the request.
16379 * Load data from the configured URL, read the data object into
16380 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16381 * process that block using the passed callback.
16382 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16383 * for the request to the remote server.
16384 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16385 * object into a block of Roo.data.Records.
16386 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16387 * The function must be passed <ul>
16388 * <li>The Record block object</li>
16389 * <li>The "arg" argument from the load function</li>
16390 * <li>A boolean success indicator</li>
16392 * @param {Object} scope The scope in which to call the callback
16393 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16395 load : function(params, reader, callback, scope, arg){
16396 if(this.fireEvent("beforeload", this, params) !== false){
16398 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16400 var url = this.url;
16401 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16403 url += "&_dc=" + (new Date().getTime());
16405 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16408 cb : "stcCallback"+transId,
16409 scriptId : "stcScript"+transId,
16413 callback : callback,
16419 window[trans.cb] = function(o){
16420 conn.handleResponse(o, trans);
16423 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16425 if(this.autoAbort !== false){
16429 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16431 var script = document.createElement("script");
16432 script.setAttribute("src", url);
16433 script.setAttribute("type", "text/javascript");
16434 script.setAttribute("id", trans.scriptId);
16435 this.head.appendChild(script);
16437 this.trans = trans;
16439 callback.call(scope||this, null, arg, false);
16444 isLoading : function(){
16445 return this.trans ? true : false;
16449 * Abort the current server request.
16451 abort : function(){
16452 if(this.isLoading()){
16453 this.destroyTrans(this.trans);
16458 destroyTrans : function(trans, isLoaded){
16459 this.head.removeChild(document.getElementById(trans.scriptId));
16460 clearTimeout(trans.timeoutId);
16462 window[trans.cb] = undefined;
16464 delete window[trans.cb];
16467 // if hasn't been loaded, wait for load to remove it to prevent script error
16468 window[trans.cb] = function(){
16469 window[trans.cb] = undefined;
16471 delete window[trans.cb];
16478 handleResponse : function(o, trans){
16479 this.trans = false;
16480 this.destroyTrans(trans, true);
16483 result = trans.reader.readRecords(o);
16485 this.fireEvent("loadexception", this, o, trans.arg, e);
16486 trans.callback.call(trans.scope||window, null, trans.arg, false);
16489 this.fireEvent("load", this, o, trans.arg);
16490 trans.callback.call(trans.scope||window, result, trans.arg, true);
16494 handleFailure : function(trans){
16495 this.trans = false;
16496 this.destroyTrans(trans, false);
16497 this.fireEvent("loadexception", this, null, trans.arg);
16498 trans.callback.call(trans.scope||window, null, trans.arg, false);
16502 * Ext JS Library 1.1.1
16503 * Copyright(c) 2006-2007, Ext JS, LLC.
16505 * Originally Released Under LGPL - original licence link has changed is not relivant.
16508 * <script type="text/javascript">
16512 * @class Roo.data.JsonReader
16513 * @extends Roo.data.DataReader
16514 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16515 * based on mappings in a provided Roo.data.Record constructor.
16517 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16518 * in the reply previously.
16523 var RecordDef = Roo.data.Record.create([
16524 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16525 {name: 'occupation'} // This field will use "occupation" as the mapping.
16527 var myReader = new Roo.data.JsonReader({
16528 totalProperty: "results", // The property which contains the total dataset size (optional)
16529 root: "rows", // The property which contains an Array of row objects
16530 id: "id" // The property within each row object that provides an ID for the record (optional)
16534 * This would consume a JSON file like this:
16536 { 'results': 2, 'rows': [
16537 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16538 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16541 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16542 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16543 * paged from the remote server.
16544 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16545 * @cfg {String} root name of the property which contains the Array of row objects.
16546 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16547 * @cfg {Array} fields Array of field definition objects
16549 * Create a new JsonReader
16550 * @param {Object} meta Metadata configuration options
16551 * @param {Object} recordType Either an Array of field definition objects,
16552 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16554 Roo.data.JsonReader = function(meta, recordType){
16557 // set some defaults:
16558 Roo.applyIf(meta, {
16559 totalProperty: 'total',
16560 successProperty : 'success',
16565 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16567 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16569 readerType : 'Json',
16572 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16573 * Used by Store query builder to append _requestMeta to params.
16576 metaFromRemote : false,
16578 * This method is only used by a DataProxy which has retrieved data from a remote server.
16579 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16580 * @return {Object} data A data block which is used by an Roo.data.Store object as
16581 * a cache of Roo.data.Records.
16583 read : function(response){
16584 var json = response.responseText;
16586 var o = /* eval:var:o */ eval("("+json+")");
16588 throw {message: "JsonReader.read: Json object not found"};
16594 this.metaFromRemote = true;
16595 this.meta = o.metaData;
16596 this.recordType = Roo.data.Record.create(o.metaData.fields);
16597 this.onMetaChange(this.meta, this.recordType, o);
16599 return this.readRecords(o);
16602 // private function a store will implement
16603 onMetaChange : function(meta, recordType, o){
16610 simpleAccess: function(obj, subsc) {
16617 getJsonAccessor: function(){
16619 return function(expr) {
16621 return(re.test(expr))
16622 ? new Function("obj", "return obj." + expr)
16627 return Roo.emptyFn;
16632 * Create a data block containing Roo.data.Records from an XML document.
16633 * @param {Object} o An object which contains an Array of row objects in the property specified
16634 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16635 * which contains the total size of the dataset.
16636 * @return {Object} data A data block which is used by an Roo.data.Store object as
16637 * a cache of Roo.data.Records.
16639 readRecords : function(o){
16641 * After any data loads, the raw JSON data is available for further custom processing.
16645 var s = this.meta, Record = this.recordType,
16646 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16648 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16650 if(s.totalProperty) {
16651 this.getTotal = this.getJsonAccessor(s.totalProperty);
16653 if(s.successProperty) {
16654 this.getSuccess = this.getJsonAccessor(s.successProperty);
16656 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16658 var g = this.getJsonAccessor(s.id);
16659 this.getId = function(rec) {
16661 return (r === undefined || r === "") ? null : r;
16664 this.getId = function(){return null;};
16667 for(var jj = 0; jj < fl; jj++){
16669 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16670 this.ef[jj] = this.getJsonAccessor(map);
16674 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16675 if(s.totalProperty){
16676 var vt = parseInt(this.getTotal(o), 10);
16681 if(s.successProperty){
16682 var vs = this.getSuccess(o);
16683 if(vs === false || vs === 'false'){
16688 for(var i = 0; i < c; i++){
16691 var id = this.getId(n);
16692 for(var j = 0; j < fl; j++){
16694 var v = this.ef[j](n);
16696 Roo.log('missing convert for ' + f.name);
16700 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16704 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16710 var record = new Record(values, id);
16712 records[i] = record;
16718 totalRecords : totalRecords
16721 // used when loading children.. @see loadDataFromChildren
16722 toLoadData: function(rec)
16724 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16725 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16726 return { data : data, total : data.length };
16731 * Ext JS Library 1.1.1
16732 * Copyright(c) 2006-2007, Ext JS, LLC.
16734 * Originally Released Under LGPL - original licence link has changed is not relivant.
16737 * <script type="text/javascript">
16741 * @class Roo.data.ArrayReader
16742 * @extends Roo.data.DataReader
16743 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16744 * Each element of that Array represents a row of data fields. The
16745 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16746 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16750 var RecordDef = Roo.data.Record.create([
16751 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16752 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16754 var myReader = new Roo.data.ArrayReader({
16755 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16759 * This would consume an Array like this:
16761 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16765 * Create a new JsonReader
16766 * @param {Object} meta Metadata configuration options.
16767 * @param {Object|Array} recordType Either an Array of field definition objects
16769 * @cfg {Array} fields Array of field definition objects
16770 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16771 * as specified to {@link Roo.data.Record#create},
16772 * or an {@link Roo.data.Record} object
16775 * created using {@link Roo.data.Record#create}.
16777 Roo.data.ArrayReader = function(meta, recordType)
16779 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16782 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16785 * Create a data block containing Roo.data.Records from an XML document.
16786 * @param {Object} o An Array of row objects which represents the dataset.
16787 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16788 * a cache of Roo.data.Records.
16790 readRecords : function(o)
16792 var sid = this.meta ? this.meta.id : null;
16793 var recordType = this.recordType, fields = recordType.prototype.fields;
16796 for(var i = 0; i < root.length; i++){
16799 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16800 for(var j = 0, jlen = fields.length; j < jlen; j++){
16801 var f = fields.items[j];
16802 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16803 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16805 values[f.name] = v;
16807 var record = new recordType(values, id);
16809 records[records.length] = record;
16813 totalRecords : records.length
16816 // used when loading children.. @see loadDataFromChildren
16817 toLoadData: function(rec)
16819 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16820 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16831 * @class Roo.bootstrap.form.ComboBox
16832 * @extends Roo.bootstrap.form.TriggerField
16833 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16834 * @cfg {Boolean} append (true|false) default false
16835 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16836 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16837 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16838 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16839 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16840 * @cfg {Boolean} animate default true
16841 * @cfg {Boolean} emptyResultText only for touch device
16842 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16843 * @cfg {String} emptyTitle default ''
16844 * @cfg {Number} width fixed with? experimental
16846 * Create a new ComboBox.
16847 * @param {Object} config Configuration options
16849 Roo.bootstrap.form.ComboBox = function(config){
16850 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16854 * Fires when the dropdown list is expanded
16855 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16860 * Fires when the dropdown list is collapsed
16861 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16865 * @event beforeselect
16866 * Fires before a list item is selected. Return false to cancel the selection.
16867 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16868 * @param {Roo.data.Record} record The data record returned from the underlying store
16869 * @param {Number} index The index of the selected item in the dropdown list
16871 'beforeselect' : true,
16874 * Fires when a list item is selected
16875 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16876 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16877 * @param {Number} index The index of the selected item in the dropdown list
16881 * @event beforequery
16882 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16883 * The event object passed has these properties:
16884 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16885 * @param {String} query The query
16886 * @param {Boolean} forceAll true to force "all" query
16887 * @param {Boolean} cancel true to cancel the query
16888 * @param {Object} e The query event object
16890 'beforequery': true,
16893 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16894 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16899 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16900 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16901 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16906 * Fires when the remove value from the combobox array
16907 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16911 * @event afterremove
16912 * Fires when the remove value from the combobox array
16913 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915 'afterremove' : true,
16917 * @event specialfilter
16918 * Fires when specialfilter
16919 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921 'specialfilter' : true,
16924 * Fires when tick the element
16925 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16929 * @event touchviewdisplay
16930 * Fires when touch view require special display (default is using displayField)
16931 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16932 * @param {Object} cfg set html .
16934 'touchviewdisplay' : true
16939 this.tickItems = [];
16941 this.selectedIndex = -1;
16942 if(this.mode == 'local'){
16943 if(config.queryDelay === undefined){
16944 this.queryDelay = 10;
16946 if(config.minChars === undefined){
16952 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16955 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16956 * rendering into an Roo.Editor, defaults to false)
16959 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16960 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16963 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16966 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16967 * the dropdown list (defaults to undefined, with no header element)
16971 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16975 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16977 listWidth: undefined,
16979 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16980 * mode = 'remote' or 'text' if mode = 'local')
16982 displayField: undefined,
16985 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16986 * mode = 'remote' or 'value' if mode = 'local').
16987 * Note: use of a valueField requires the user make a selection
16988 * in order for a value to be mapped.
16990 valueField: undefined,
16992 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16997 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16998 * field's data value (defaults to the underlying DOM element's name)
17000 hiddenName: undefined,
17002 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17006 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17008 selectedClass: 'active',
17011 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17015 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17016 * anchor positions (defaults to 'tl-bl')
17018 listAlign: 'tl-bl?',
17020 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17024 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17025 * query specified by the allQuery config option (defaults to 'query')
17027 triggerAction: 'query',
17029 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17030 * (defaults to 4, does not apply if editable = false)
17034 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17035 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17039 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17040 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17044 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17045 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17049 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17050 * when editable = true (defaults to false)
17052 selectOnFocus:false,
17054 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17056 queryParam: 'query',
17058 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17059 * when mode = 'remote' (defaults to 'Loading...')
17061 loadingText: 'Loading...',
17063 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17067 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17071 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17072 * traditional select (defaults to true)
17076 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17080 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17084 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17085 * listWidth has a higher value)
17089 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17090 * allow the user to set arbitrary text into the field (defaults to false)
17092 forceSelection:false,
17094 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17095 * if typeAhead = true (defaults to 250)
17097 typeAheadDelay : 250,
17099 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17100 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17102 valueNotFoundText : undefined,
17104 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17106 blockFocus : false,
17109 * @cfg {Boolean} disableClear Disable showing of clear button.
17111 disableClear : false,
17113 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17115 alwaysQuery : false,
17118 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17123 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17125 invalidClass : "has-warning",
17128 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17130 validClass : "has-success",
17133 * @cfg {Boolean} specialFilter (true|false) special filter default false
17135 specialFilter : false,
17138 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17140 mobileTouchView : true,
17143 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17145 useNativeIOS : false,
17148 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17150 mobile_restrict_height : false,
17152 ios_options : false,
17164 btnPosition : 'right',
17165 triggerList : true,
17166 showToggleBtn : true,
17168 emptyResultText: 'Empty',
17169 triggerText : 'Select',
17173 // element that contains real text value.. (when hidden is used..)
17175 getAutoCreate : function()
17180 * Render classic select for iso
17183 if(Roo.isIOS && this.useNativeIOS){
17184 cfg = this.getAutoCreateNativeIOS();
17192 if(Roo.isTouch && this.mobileTouchView){
17193 cfg = this.getAutoCreateTouchView();
17200 if(!this.tickable){
17201 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17206 * ComboBox with tickable selections
17209 var align = this.labelAlign || this.parentLabelAlign();
17212 cls : 'form-group roo-combobox-tickable' //input-group
17215 var btn_text_select = '';
17216 var btn_text_done = '';
17217 var btn_text_cancel = '';
17219 if (this.btn_text_show) {
17220 btn_text_select = 'Select';
17221 btn_text_done = 'Done';
17222 btn_text_cancel = 'Cancel';
17227 cls : 'tickable-buttons',
17232 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17233 //html : this.triggerText
17234 html: btn_text_select
17240 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17242 html: btn_text_done
17248 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17250 html: btn_text_cancel
17256 buttons.cn.unshift({
17258 cls: 'roo-select2-search-field-input'
17264 Roo.each(buttons.cn, function(c){
17266 c.cls += ' btn-' + _this.size;
17269 if (_this.disabled) {
17276 style : 'display: contents',
17281 cls: 'form-hidden-field'
17285 cls: 'roo-select2-choices',
17289 cls: 'roo-select2-search-field',
17300 cls: 'roo-select2-container input-group roo-select2-container-multi',
17306 // cls: 'typeahead typeahead-long dropdown-menu',
17307 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17312 if(this.hasFeedback && !this.allowBlank){
17316 cls: 'glyphicon form-control-feedback'
17319 combobox.cn.push(feedback);
17326 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17327 tooltip : 'This field is required'
17330 if (this.allowBlank) {
17333 style : 'display:none'
17336 if (align ==='left' && this.fieldLabel.length) {
17338 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17345 cls : 'control-label col-form-label',
17346 html : this.fieldLabel
17358 var labelCfg = cfg.cn[1];
17359 var contentCfg = cfg.cn[2];
17362 if(this.indicatorpos == 'right'){
17368 cls : 'control-label col-form-label',
17372 html : this.fieldLabel
17388 labelCfg = cfg.cn[0];
17389 contentCfg = cfg.cn[1];
17393 if(this.labelWidth > 12){
17394 labelCfg.style = "width: " + this.labelWidth + 'px';
17396 if(this.width * 1 > 0){
17397 contentCfg.style = "width: " + this.width + 'px';
17399 if(this.labelWidth < 13 && this.labelmd == 0){
17400 this.labelmd = this.labelWidth;
17403 if(this.labellg > 0){
17404 labelCfg.cls += ' col-lg-' + this.labellg;
17405 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17408 if(this.labelmd > 0){
17409 labelCfg.cls += ' col-md-' + this.labelmd;
17410 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17413 if(this.labelsm > 0){
17414 labelCfg.cls += ' col-sm-' + this.labelsm;
17415 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17418 if(this.labelxs > 0){
17419 labelCfg.cls += ' col-xs-' + this.labelxs;
17420 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17424 } else if ( this.fieldLabel.length) {
17425 // Roo.log(" label");
17430 //cls : 'input-group-addon',
17431 html : this.fieldLabel
17436 if(this.indicatorpos == 'right'){
17440 //cls : 'input-group-addon',
17441 html : this.fieldLabel
17451 // Roo.log(" no label && no align");
17458 ['xs','sm','md','lg'].map(function(size){
17459 if (settings[size]) {
17460 cfg.cls += ' col-' + size + '-' + settings[size];
17468 _initEventsCalled : false,
17471 initEvents: function()
17473 if (this._initEventsCalled) { // as we call render... prevent looping...
17476 this._initEventsCalled = true;
17479 throw "can not find store for combo";
17482 this.indicator = this.indicatorEl();
17484 this.store = Roo.factory(this.store, Roo.data);
17485 this.store.parent = this;
17487 // if we are building from html. then this element is so complex, that we can not really
17488 // use the rendered HTML.
17489 // so we have to trash and replace the previous code.
17490 if (Roo.XComponent.build_from_html) {
17491 // remove this element....
17492 var e = this.el.dom, k=0;
17493 while (e ) { e = e.previousSibling; ++k;}
17498 this.rendered = false;
17500 this.render(this.parent().getChildContainer(true), k);
17503 if(Roo.isIOS && this.useNativeIOS){
17504 this.initIOSView();
17512 if(Roo.isTouch && this.mobileTouchView){
17513 this.initTouchView();
17518 this.initTickableEvents();
17522 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17524 if(this.hiddenName){
17526 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17528 this.hiddenField.dom.value =
17529 this.hiddenValue !== undefined ? this.hiddenValue :
17530 this.value !== undefined ? this.value : '';
17532 // prevent input submission
17533 this.el.dom.removeAttribute('name');
17534 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17539 // this.el.dom.setAttribute('autocomplete', 'off');
17542 var cls = 'x-combo-list';
17544 //this.list = new Roo.Layer({
17545 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17551 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17552 _this.list.setWidth(lw);
17555 this.list.on('mouseover', this.onViewOver, this);
17556 this.list.on('mousemove', this.onViewMove, this);
17557 this.list.on('scroll', this.onViewScroll, this);
17560 this.list.swallowEvent('mousewheel');
17561 this.assetHeight = 0;
17564 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17565 this.assetHeight += this.header.getHeight();
17568 this.innerList = this.list.createChild({cls:cls+'-inner'});
17569 this.innerList.on('mouseover', this.onViewOver, this);
17570 this.innerList.on('mousemove', this.onViewMove, this);
17571 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17573 if(this.allowBlank && !this.pageSize && !this.disableClear){
17574 this.footer = this.list.createChild({cls:cls+'-ft'});
17575 this.pageTb = new Roo.Toolbar(this.footer);
17579 this.footer = this.list.createChild({cls:cls+'-ft'});
17580 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17581 {pageSize: this.pageSize});
17585 if (this.pageTb && this.allowBlank && !this.disableClear) {
17587 this.pageTb.add(new Roo.Toolbar.Fill(), {
17588 cls: 'x-btn-icon x-btn-clear',
17590 handler: function()
17593 _this.clearValue();
17594 _this.onSelect(false, -1);
17599 this.assetHeight += this.footer.getHeight();
17604 this.tpl = Roo.bootstrap.version == 4 ?
17605 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17606 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17609 this.view = new Roo.View(this.list, this.tpl, {
17610 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17612 //this.view.wrapEl.setDisplayed(false);
17613 this.view.on('click', this.onViewClick, this);
17616 this.store.on('beforeload', this.onBeforeLoad, this);
17617 this.store.on('load', this.onLoad, this);
17618 this.store.on('loadexception', this.onLoadException, this);
17620 if(this.resizable){
17621 this.resizer = new Roo.Resizable(this.list, {
17622 pinned:true, handles:'se'
17624 this.resizer.on('resize', function(r, w, h){
17625 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17626 this.listWidth = w;
17627 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17628 this.restrictHeight();
17630 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17633 if(!this.editable){
17634 this.editable = true;
17635 this.setEditable(false);
17640 if (typeof(this.events.add.listeners) != 'undefined') {
17642 this.addicon = this.wrap.createChild(
17643 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17645 this.addicon.on('click', function(e) {
17646 this.fireEvent('add', this);
17649 if (typeof(this.events.edit.listeners) != 'undefined') {
17651 this.editicon = this.wrap.createChild(
17652 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17653 if (this.addicon) {
17654 this.editicon.setStyle('margin-left', '40px');
17656 this.editicon.on('click', function(e) {
17658 // we fire even if inothing is selected..
17659 this.fireEvent('edit', this, this.lastData );
17665 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17666 "up" : function(e){
17667 this.inKeyMode = true;
17671 "down" : function(e){
17672 if(!this.isExpanded()){
17673 this.onTriggerClick();
17675 this.inKeyMode = true;
17680 "enter" : function(e){
17681 // this.onViewClick();
17685 if(this.fireEvent("specialkey", this, e)){
17686 this.onViewClick(false);
17692 "esc" : function(e){
17696 "tab" : function(e){
17699 if(this.fireEvent("specialkey", this, e)){
17700 this.onViewClick(false);
17708 doRelay : function(foo, bar, hname){
17709 if(hname == 'down' || this.scope.isExpanded()){
17710 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17719 this.queryDelay = Math.max(this.queryDelay || 10,
17720 this.mode == 'local' ? 10 : 250);
17723 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17725 if(this.typeAhead){
17726 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17728 if(this.editable !== false){
17729 this.inputEl().on("keyup", this.onKeyUp, this);
17731 if(this.forceSelection){
17732 this.inputEl().on('blur', this.doForce, this);
17736 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17737 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17741 initTickableEvents: function()
17745 if(this.hiddenName){
17747 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17749 this.hiddenField.dom.value =
17750 this.hiddenValue !== undefined ? this.hiddenValue :
17751 this.value !== undefined ? this.value : '';
17753 // prevent input submission
17754 this.el.dom.removeAttribute('name');
17755 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17760 // this.list = this.el.select('ul.dropdown-menu',true).first();
17762 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17763 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17764 if(this.triggerList){
17765 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17768 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17769 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17771 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17772 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17774 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17775 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17777 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17778 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17779 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17782 this.cancelBtn.hide();
17787 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17788 _this.list.setWidth(lw);
17791 this.list.on('mouseover', this.onViewOver, this);
17792 this.list.on('mousemove', this.onViewMove, this);
17794 this.list.on('scroll', this.onViewScroll, this);
17797 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17798 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17801 this.view = new Roo.View(this.list, this.tpl, {
17806 selectedClass: this.selectedClass
17809 //this.view.wrapEl.setDisplayed(false);
17810 this.view.on('click', this.onViewClick, this);
17814 this.store.on('beforeload', this.onBeforeLoad, this);
17815 this.store.on('load', this.onLoad, this);
17816 this.store.on('loadexception', this.onLoadException, this);
17819 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17820 "up" : function(e){
17821 this.inKeyMode = true;
17825 "down" : function(e){
17826 this.inKeyMode = true;
17830 "enter" : function(e){
17831 if(this.fireEvent("specialkey", this, e)){
17832 this.onViewClick(false);
17838 "esc" : function(e){
17839 this.onTickableFooterButtonClick(e, false, false);
17842 "tab" : function(e){
17843 this.fireEvent("specialkey", this, e);
17845 this.onTickableFooterButtonClick(e, false, false);
17852 doRelay : function(e, fn, key){
17853 if(this.scope.isExpanded()){
17854 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17863 this.queryDelay = Math.max(this.queryDelay || 10,
17864 this.mode == 'local' ? 10 : 250);
17867 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17869 if(this.typeAhead){
17870 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17873 if(this.editable !== false){
17874 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17877 this.indicator = this.indicatorEl();
17879 if(this.indicator){
17880 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17881 this.indicator.hide();
17886 onDestroy : function(){
17888 this.view.setStore(null);
17889 this.view.el.removeAllListeners();
17890 this.view.el.remove();
17891 this.view.purgeListeners();
17894 this.list.dom.innerHTML = '';
17898 this.store.un('beforeload', this.onBeforeLoad, this);
17899 this.store.un('load', this.onLoad, this);
17900 this.store.un('loadexception', this.onLoadException, this);
17902 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17906 fireKey : function(e){
17907 if(e.isNavKeyPress() && !this.list.isVisible()){
17908 this.fireEvent("specialkey", this, e);
17913 onResize: function(w, h)
17917 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17919 // if(typeof w != 'number'){
17920 // // we do not handle it!?!?
17923 // var tw = this.trigger.getWidth();
17924 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17925 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17927 // this.inputEl().setWidth( this.adjustWidth('input', x));
17929 // //this.trigger.setStyle('left', x+'px');
17931 // if(this.list && this.listWidth === undefined){
17932 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17933 // this.list.setWidth(lw);
17934 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17942 * Allow or prevent the user from directly editing the field text. If false is passed,
17943 * the user will only be able to select from the items defined in the dropdown list. This method
17944 * is the runtime equivalent of setting the 'editable' config option at config time.
17945 * @param {Boolean} value True to allow the user to directly edit the field text
17947 setEditable : function(value){
17948 if(value == this.editable){
17951 this.editable = value;
17953 this.inputEl().dom.setAttribute('readOnly', true);
17954 this.inputEl().on('mousedown', this.onTriggerClick, this);
17955 this.inputEl().addClass('x-combo-noedit');
17957 this.inputEl().dom.removeAttribute('readOnly');
17958 this.inputEl().un('mousedown', this.onTriggerClick, this);
17959 this.inputEl().removeClass('x-combo-noedit');
17965 onBeforeLoad : function(combo,opts){
17966 if(!this.hasFocus){
17970 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17972 this.restrictHeight();
17973 this.selectedIndex = -1;
17977 onLoad : function(){
17979 this.hasQuery = false;
17981 if(!this.hasFocus){
17985 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17986 this.loading.hide();
17989 if(this.store.getCount() > 0){
17992 this.restrictHeight();
17993 if(this.lastQuery == this.allQuery){
17994 if(this.editable && !this.tickable){
17995 this.inputEl().dom.select();
17999 !this.selectByValue(this.value, true) &&
18002 !this.store.lastOptions ||
18003 typeof(this.store.lastOptions.add) == 'undefined' ||
18004 this.store.lastOptions.add != true
18007 this.select(0, true);
18010 if(this.autoFocus){
18013 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18014 this.taTask.delay(this.typeAheadDelay);
18018 this.onEmptyResults();
18024 onLoadException : function()
18026 this.hasQuery = false;
18028 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18029 this.loading.hide();
18032 if(this.tickable && this.editable){
18037 // only causes errors at present
18038 //Roo.log(this.store.reader.jsonData);
18039 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18041 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18047 onTypeAhead : function(){
18048 if(this.store.getCount() > 0){
18049 var r = this.store.getAt(0);
18050 var newValue = r.data[this.displayField];
18051 var len = newValue.length;
18052 var selStart = this.getRawValue().length;
18054 if(selStart != len){
18055 this.setRawValue(newValue);
18056 this.selectText(selStart, newValue.length);
18062 onSelect : function(record, index){
18064 if(this.fireEvent('beforeselect', this, record, index) !== false){
18066 this.setFromData(index > -1 ? record.data : false);
18069 this.fireEvent('select', this, record, index);
18074 * Returns the currently selected field value or empty string if no value is set.
18075 * @return {String} value The selected value
18077 getValue : function()
18079 if(Roo.isIOS && this.useNativeIOS){
18080 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18084 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18087 if(this.valueField){
18088 return typeof this.value != 'undefined' ? this.value : '';
18090 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18094 getRawValue : function()
18096 if(Roo.isIOS && this.useNativeIOS){
18097 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18100 var v = this.inputEl().getValue();
18106 * Clears any text/value currently set in the field
18108 clearValue : function(){
18110 if(this.hiddenField){
18111 this.hiddenField.dom.value = '';
18114 this.setRawValue('');
18115 this.lastSelectionText = '';
18116 this.lastData = false;
18118 var close = this.closeTriggerEl();
18129 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18130 * will be displayed in the field. If the value does not match the data value of an existing item,
18131 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18132 * Otherwise the field will be blank (although the value will still be set).
18133 * @param {String} value The value to match
18135 setValue : function(v)
18137 if(Roo.isIOS && this.useNativeIOS){
18138 this.setIOSValue(v);
18148 if(this.valueField){
18149 var r = this.findRecord(this.valueField, v);
18151 text = r.data[this.displayField];
18152 }else if(this.valueNotFoundText !== undefined){
18153 text = this.valueNotFoundText;
18156 this.lastSelectionText = text;
18157 if(this.hiddenField){
18158 this.hiddenField.dom.value = v;
18160 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18163 var close = this.closeTriggerEl();
18166 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18172 * @property {Object} the last set data for the element
18177 * Sets the value of the field based on a object which is related to the record format for the store.
18178 * @param {Object} value the value to set as. or false on reset?
18180 setFromData : function(o){
18187 var dv = ''; // display value
18188 var vv = ''; // value value..
18190 if (this.displayField) {
18191 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18193 // this is an error condition!!!
18194 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18197 if(this.valueField){
18198 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18201 var close = this.closeTriggerEl();
18204 if(dv.length || vv * 1 > 0){
18206 this.blockFocus=true;
18212 if(this.hiddenField){
18213 this.hiddenField.dom.value = vv;
18215 this.lastSelectionText = dv;
18216 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18220 // no hidden field.. - we store the value in 'value', but still display
18221 // display field!!!!
18222 this.lastSelectionText = dv;
18223 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18230 reset : function(){
18231 // overridden so that last data is reset..
18238 this.setValue(this.originalValue);
18239 //this.clearInvalid();
18240 this.lastData = false;
18242 this.view.clearSelections();
18248 findRecord : function(prop, value){
18250 if(this.store.getCount() > 0){
18251 this.store.each(function(r){
18252 if(r.data[prop] == value){
18262 getName: function()
18264 // returns hidden if it's set..
18265 if (!this.rendered) {return ''};
18266 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18270 onViewMove : function(e, t){
18271 this.inKeyMode = false;
18275 onViewOver : function(e, t){
18276 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18279 var item = this.view.findItemFromChild(t);
18282 var index = this.view.indexOf(item);
18283 this.select(index, false);
18288 onViewClick : function(view, doFocus, el, e)
18290 var index = this.view.getSelectedIndexes()[0];
18292 var r = this.store.getAt(index);
18296 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18303 Roo.each(this.tickItems, function(v,k){
18305 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18307 _this.tickItems.splice(k, 1);
18309 if(typeof(e) == 'undefined' && view == false){
18310 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18322 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18323 this.tickItems.push(r.data);
18326 if(typeof(e) == 'undefined' && view == false){
18327 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18334 this.onSelect(r, index);
18336 if(doFocus !== false && !this.blockFocus){
18337 this.inputEl().focus();
18342 restrictHeight : function(){
18343 //this.innerList.dom.style.height = '';
18344 //var inner = this.innerList.dom;
18345 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18346 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18347 //this.list.beginUpdate();
18348 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18349 this.list.alignTo(this.inputEl(), this.listAlign);
18350 this.list.alignTo(this.inputEl(), this.listAlign);
18351 //this.list.endUpdate();
18355 onEmptyResults : function(){
18357 if(this.tickable && this.editable){
18358 this.hasFocus = false;
18359 this.restrictHeight();
18367 * Returns true if the dropdown list is expanded, else false.
18369 isExpanded : function(){
18370 return this.list.isVisible();
18374 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18375 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18376 * @param {String} value The data value of the item to select
18377 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18378 * selected item if it is not currently in view (defaults to true)
18379 * @return {Boolean} True if the value matched an item in the list, else false
18381 selectByValue : function(v, scrollIntoView){
18382 if(v !== undefined && v !== null){
18383 var r = this.findRecord(this.valueField || this.displayField, v);
18385 this.select(this.store.indexOf(r), scrollIntoView);
18393 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18394 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18395 * @param {Number} index The zero-based index of the list item to select
18396 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18397 * selected item if it is not currently in view (defaults to true)
18399 select : function(index, scrollIntoView){
18400 this.selectedIndex = index;
18401 this.view.select(index);
18402 if(scrollIntoView !== false){
18403 var el = this.view.getNode(index);
18405 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18408 this.list.scrollChildIntoView(el, false);
18414 selectNext : function(){
18415 var ct = this.store.getCount();
18417 if(this.selectedIndex == -1){
18419 }else if(this.selectedIndex < ct-1){
18420 this.select(this.selectedIndex+1);
18426 selectPrev : function(){
18427 var ct = this.store.getCount();
18429 if(this.selectedIndex == -1){
18431 }else if(this.selectedIndex != 0){
18432 this.select(this.selectedIndex-1);
18438 onKeyUp : function(e){
18439 if(this.editable !== false && !e.isSpecialKey()){
18440 this.lastKey = e.getKey();
18441 this.dqTask.delay(this.queryDelay);
18446 validateBlur : function(){
18447 return !this.list || !this.list.isVisible();
18451 initQuery : function(){
18453 var v = this.getRawValue();
18455 if(this.tickable && this.editable){
18456 v = this.tickableInputEl().getValue();
18463 doForce : function(){
18464 if(this.inputEl().dom.value.length > 0){
18465 this.inputEl().dom.value =
18466 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18472 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18473 * query allowing the query action to be canceled if needed.
18474 * @param {String} query The SQL query to execute
18475 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18476 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18477 * saved in the current store (defaults to false)
18479 doQuery : function(q, forceAll){
18481 if(q === undefined || q === null){
18486 forceAll: forceAll,
18490 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18495 forceAll = qe.forceAll;
18496 if(forceAll === true || (q.length >= this.minChars)){
18498 this.hasQuery = true;
18500 if(this.lastQuery != q || this.alwaysQuery){
18501 this.lastQuery = q;
18502 if(this.mode == 'local'){
18503 this.selectedIndex = -1;
18505 this.store.clearFilter();
18508 if(this.specialFilter){
18509 this.fireEvent('specialfilter', this);
18514 this.store.filter(this.displayField, q);
18517 this.store.fireEvent("datachanged", this.store);
18524 this.store.baseParams[this.queryParam] = q;
18526 var options = {params : this.getParams(q)};
18529 options.add = true;
18530 options.params.start = this.page * this.pageSize;
18533 this.store.load(options);
18536 * this code will make the page width larger, at the beginning, the list not align correctly,
18537 * we should expand the list on onLoad
18538 * so command out it
18543 this.selectedIndex = -1;
18548 this.loadNext = false;
18552 getParams : function(q){
18554 //p[this.queryParam] = q;
18558 p.limit = this.pageSize;
18564 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18566 collapse : function(){
18567 if(!this.isExpanded()){
18573 this.hasFocus = false;
18577 this.cancelBtn.hide();
18578 this.trigger.show();
18581 this.tickableInputEl().dom.value = '';
18582 this.tickableInputEl().blur();
18587 Roo.get(document).un('mousedown', this.collapseIf, this);
18588 Roo.get(document).un('mousewheel', this.collapseIf, this);
18589 if (!this.editable) {
18590 Roo.get(document).un('keydown', this.listKeyPress, this);
18592 this.fireEvent('collapse', this);
18598 collapseIf : function(e){
18599 var in_combo = e.within(this.el);
18600 var in_list = e.within(this.list);
18601 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18603 if (in_combo || in_list || is_list) {
18604 //e.stopPropagation();
18609 this.onTickableFooterButtonClick(e, false, false);
18617 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18619 expand : function(){
18621 if(this.isExpanded() || !this.hasFocus){
18625 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18626 this.list.setWidth(lw);
18632 this.restrictHeight();
18636 this.tickItems = Roo.apply([], this.item);
18639 this.cancelBtn.show();
18640 this.trigger.hide();
18643 this.tickableInputEl().focus();
18648 Roo.get(document).on('mousedown', this.collapseIf, this);
18649 Roo.get(document).on('mousewheel', this.collapseIf, this);
18650 if (!this.editable) {
18651 Roo.get(document).on('keydown', this.listKeyPress, this);
18654 this.fireEvent('expand', this);
18658 // Implements the default empty TriggerField.onTriggerClick function
18659 onTriggerClick : function(e)
18661 Roo.log('trigger click');
18663 if(this.disabled || !this.triggerList){
18668 this.loadNext = false;
18670 if(this.isExpanded()){
18672 if (!this.blockFocus) {
18673 this.inputEl().focus();
18677 this.hasFocus = true;
18678 if(this.triggerAction == 'all') {
18679 this.doQuery(this.allQuery, true);
18681 this.doQuery(this.getRawValue());
18683 if (!this.blockFocus) {
18684 this.inputEl().focus();
18689 onTickableTriggerClick : function(e)
18696 this.loadNext = false;
18697 this.hasFocus = true;
18699 if(this.triggerAction == 'all') {
18700 this.doQuery(this.allQuery, true);
18702 this.doQuery(this.getRawValue());
18706 onSearchFieldClick : function(e)
18708 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18709 this.onTickableFooterButtonClick(e, false, false);
18713 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18718 this.loadNext = false;
18719 this.hasFocus = true;
18721 if(this.triggerAction == 'all') {
18722 this.doQuery(this.allQuery, true);
18724 this.doQuery(this.getRawValue());
18728 listKeyPress : function(e)
18730 //Roo.log('listkeypress');
18731 // scroll to first matching element based on key pres..
18732 if (e.isSpecialKey()) {
18735 var k = String.fromCharCode(e.getKey()).toUpperCase();
18738 var csel = this.view.getSelectedNodes();
18739 var cselitem = false;
18741 var ix = this.view.indexOf(csel[0]);
18742 cselitem = this.store.getAt(ix);
18743 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18749 this.store.each(function(v) {
18751 // start at existing selection.
18752 if (cselitem.id == v.id) {
18758 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18759 match = this.store.indexOf(v);
18765 if (match === false) {
18766 return true; // no more action?
18769 this.view.select(match);
18770 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18771 sn.scrollIntoView(sn.dom.parentNode, false);
18774 onViewScroll : function(e, t){
18776 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){
18780 this.hasQuery = true;
18782 this.loading = this.list.select('.loading', true).first();
18784 if(this.loading === null){
18785 this.list.createChild({
18787 cls: 'loading roo-select2-more-results roo-select2-active',
18788 html: 'Loading more results...'
18791 this.loading = this.list.select('.loading', true).first();
18793 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18795 this.loading.hide();
18798 this.loading.show();
18803 this.loadNext = true;
18805 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18810 addItem : function(o)
18812 var dv = ''; // display value
18814 if (this.displayField) {
18815 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18817 // this is an error condition!!!
18818 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18825 var choice = this.choices.createChild({
18827 cls: 'roo-select2-search-choice',
18836 cls: 'roo-select2-search-choice-close fa fa-times',
18841 }, this.searchField);
18843 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18845 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18853 this.inputEl().dom.value = '';
18858 onRemoveItem : function(e, _self, o)
18860 e.preventDefault();
18862 this.lastItem = Roo.apply([], this.item);
18864 var index = this.item.indexOf(o.data) * 1;
18867 Roo.log('not this item?!');
18871 this.item.splice(index, 1);
18876 this.fireEvent('remove', this, e);
18882 syncValue : function()
18884 if(!this.item.length){
18891 Roo.each(this.item, function(i){
18892 if(_this.valueField){
18893 value.push(i[_this.valueField]);
18900 this.value = value.join(',');
18902 if(this.hiddenField){
18903 this.hiddenField.dom.value = this.value;
18906 this.store.fireEvent("datachanged", this.store);
18911 clearItem : function()
18913 if(!this.multiple){
18919 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18927 if(this.tickable && !Roo.isTouch){
18928 this.view.refresh();
18932 inputEl: function ()
18934 if(Roo.isIOS && this.useNativeIOS){
18935 return this.el.select('select.roo-ios-select', true).first();
18938 if(Roo.isTouch && this.mobileTouchView){
18939 return this.el.select('input.form-control',true).first();
18943 return this.searchField;
18946 return this.el.select('input.form-control',true).first();
18949 onTickableFooterButtonClick : function(e, btn, el)
18951 e.preventDefault();
18953 this.lastItem = Roo.apply([], this.item);
18955 if(btn && btn.name == 'cancel'){
18956 this.tickItems = Roo.apply([], this.item);
18965 Roo.each(this.tickItems, function(o){
18973 validate : function()
18975 if(this.getVisibilityEl().hasClass('hidden')){
18979 var v = this.getRawValue();
18982 v = this.getValue();
18985 if(this.disabled || this.allowBlank || v.length){
18990 this.markInvalid();
18994 tickableInputEl : function()
18996 if(!this.tickable || !this.editable){
18997 return this.inputEl();
19000 return this.inputEl().select('.roo-select2-search-field-input', true).first();
19004 getAutoCreateTouchView : function()
19009 cls: 'form-group' //input-group
19015 type : this.inputType,
19016 cls : 'form-control x-combo-noedit',
19017 autocomplete: 'new-password',
19018 placeholder : this.placeholder || '',
19023 input.name = this.name;
19027 input.cls += ' input-' + this.size;
19030 if (this.disabled) {
19031 input.disabled = true;
19035 cls : 'roo-combobox-wrap',
19042 inputblock.cls += ' input-group';
19044 inputblock.cn.unshift({
19046 cls : 'input-group-addon input-group-prepend input-group-text',
19051 if(this.removable && !this.multiple){
19052 inputblock.cls += ' roo-removable';
19054 inputblock.cn.push({
19057 cls : 'roo-combo-removable-btn close'
19061 if(this.hasFeedback && !this.allowBlank){
19063 inputblock.cls += ' has-feedback';
19065 inputblock.cn.push({
19067 cls: 'glyphicon form-control-feedback'
19074 inputblock.cls += (this.before) ? '' : ' input-group';
19076 inputblock.cn.push({
19078 cls : 'input-group-addon input-group-append input-group-text',
19084 var ibwrap = inputblock;
19089 cls: 'roo-select2-choices',
19093 cls: 'roo-select2-search-field',
19106 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19111 cls: 'form-hidden-field'
19117 if(!this.multiple && this.showToggleBtn){
19123 if (this.caret != false) {
19126 cls: 'fa fa-' + this.caret
19133 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19135 Roo.bootstrap.version == 3 ? caret : '',
19138 cls: 'combobox-clear',
19152 combobox.cls += ' roo-select2-container-multi';
19155 var required = this.allowBlank ? {
19157 style: 'display: none'
19160 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19161 tooltip : 'This field is required'
19164 var align = this.labelAlign || this.parentLabelAlign();
19166 if (align ==='left' && this.fieldLabel.length) {
19172 cls : 'control-label col-form-label',
19173 html : this.fieldLabel
19177 cls : 'roo-combobox-wrap ',
19184 var labelCfg = cfg.cn[1];
19185 var contentCfg = cfg.cn[2];
19188 if(this.indicatorpos == 'right'){
19193 cls : 'control-label col-form-label',
19197 html : this.fieldLabel
19203 cls : "roo-combobox-wrap ",
19211 labelCfg = cfg.cn[0];
19212 contentCfg = cfg.cn[1];
19217 if(this.labelWidth > 12){
19218 labelCfg.style = "width: " + this.labelWidth + 'px';
19221 if(this.labelWidth < 13 && this.labelmd == 0){
19222 this.labelmd = this.labelWidth;
19225 if(this.labellg > 0){
19226 labelCfg.cls += ' col-lg-' + this.labellg;
19227 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19230 if(this.labelmd > 0){
19231 labelCfg.cls += ' col-md-' + this.labelmd;
19232 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19235 if(this.labelsm > 0){
19236 labelCfg.cls += ' col-sm-' + this.labelsm;
19237 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19240 if(this.labelxs > 0){
19241 labelCfg.cls += ' col-xs-' + this.labelxs;
19242 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19246 } else if ( this.fieldLabel.length) {
19251 cls : 'control-label',
19252 html : this.fieldLabel
19263 if(this.indicatorpos == 'right'){
19267 cls : 'control-label',
19268 html : this.fieldLabel,
19286 var settings = this;
19288 ['xs','sm','md','lg'].map(function(size){
19289 if (settings[size]) {
19290 cfg.cls += ' col-' + size + '-' + settings[size];
19297 initTouchView : function()
19299 this.renderTouchView();
19301 this.touchViewEl.on('scroll', function(){
19302 this.el.dom.scrollTop = 0;
19305 this.originalValue = this.getValue();
19307 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19309 this.inputEl().on("click", this.showTouchView, this);
19310 if (this.triggerEl) {
19311 this.triggerEl.on("click", this.showTouchView, this);
19315 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19316 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19318 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19320 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19321 this.store.on('load', this.onTouchViewLoad, this);
19322 this.store.on('loadexception', this.onTouchViewLoadException, this);
19324 if(this.hiddenName){
19326 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19328 this.hiddenField.dom.value =
19329 this.hiddenValue !== undefined ? this.hiddenValue :
19330 this.value !== undefined ? this.value : '';
19332 this.el.dom.removeAttribute('name');
19333 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19337 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19338 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19341 if(this.removable && !this.multiple){
19342 var close = this.closeTriggerEl();
19344 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19345 close.on('click', this.removeBtnClick, this, close);
19349 * fix the bug in Safari iOS8
19351 this.inputEl().on("focus", function(e){
19352 document.activeElement.blur();
19355 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19362 renderTouchView : function()
19364 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19365 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19367 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19368 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19370 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19371 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372 this.touchViewBodyEl.setStyle('overflow', 'auto');
19374 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19375 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19377 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19378 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19382 showTouchView : function()
19388 this.touchViewHeaderEl.hide();
19390 if(this.modalTitle.length){
19391 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19392 this.touchViewHeaderEl.show();
19395 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19396 this.touchViewEl.show();
19398 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19400 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19401 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19403 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19405 if(this.modalTitle.length){
19406 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19409 this.touchViewBodyEl.setHeight(bodyHeight);
19413 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19415 this.touchViewEl.addClass(['in','show']);
19418 if(this._touchViewMask){
19419 Roo.get(document.body).addClass("x-body-masked");
19420 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19421 this._touchViewMask.setStyle('z-index', 10000);
19422 this._touchViewMask.addClass('show');
19425 this.doTouchViewQuery();
19429 hideTouchView : function()
19431 this.touchViewEl.removeClass(['in','show']);
19435 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19437 this.touchViewEl.setStyle('display', 'none');
19440 if(this._touchViewMask){
19441 this._touchViewMask.removeClass('show');
19442 Roo.get(document.body).removeClass("x-body-masked");
19446 setTouchViewValue : function()
19453 Roo.each(this.tickItems, function(o){
19458 this.hideTouchView();
19461 doTouchViewQuery : function()
19470 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19474 if(!this.alwaysQuery || this.mode == 'local'){
19475 this.onTouchViewLoad();
19482 onTouchViewBeforeLoad : function(combo,opts)
19488 onTouchViewLoad : function()
19490 if(this.store.getCount() < 1){
19491 this.onTouchViewEmptyResults();
19495 this.clearTouchView();
19497 var rawValue = this.getRawValue();
19499 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19501 this.tickItems = [];
19503 this.store.data.each(function(d, rowIndex){
19504 var row = this.touchViewListGroup.createChild(template);
19506 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19507 row.addClass(d.data.cls);
19510 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19513 html : d.data[this.displayField]
19516 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19517 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19520 row.removeClass('selected');
19521 if(!this.multiple && this.valueField &&
19522 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19525 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19526 row.addClass('selected');
19529 if(this.multiple && this.valueField &&
19530 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19534 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19535 this.tickItems.push(d.data);
19538 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19542 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19544 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19546 if(this.modalTitle.length){
19547 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19550 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19552 if(this.mobile_restrict_height && listHeight < bodyHeight){
19553 this.touchViewBodyEl.setHeight(listHeight);
19558 if(firstChecked && listHeight > bodyHeight){
19559 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19564 onTouchViewLoadException : function()
19566 this.hideTouchView();
19569 onTouchViewEmptyResults : function()
19571 this.clearTouchView();
19573 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19575 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19579 clearTouchView : function()
19581 this.touchViewListGroup.dom.innerHTML = '';
19584 onTouchViewClick : function(e, el, o)
19586 e.preventDefault();
19589 var rowIndex = o.rowIndex;
19591 var r = this.store.getAt(rowIndex);
19593 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19595 if(!this.multiple){
19596 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19597 c.dom.removeAttribute('checked');
19600 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19602 this.setFromData(r.data);
19604 var close = this.closeTriggerEl();
19610 this.hideTouchView();
19612 this.fireEvent('select', this, r, rowIndex);
19617 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19618 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19619 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19623 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19624 this.addItem(r.data);
19625 this.tickItems.push(r.data);
19629 getAutoCreateNativeIOS : function()
19632 cls: 'form-group' //input-group,
19637 cls : 'roo-ios-select'
19641 combobox.name = this.name;
19644 if (this.disabled) {
19645 combobox.disabled = true;
19648 var settings = this;
19650 ['xs','sm','md','lg'].map(function(size){
19651 if (settings[size]) {
19652 cfg.cls += ' col-' + size + '-' + settings[size];
19662 initIOSView : function()
19664 this.store.on('load', this.onIOSViewLoad, this);
19669 onIOSViewLoad : function()
19671 if(this.store.getCount() < 1){
19675 this.clearIOSView();
19677 if(this.allowBlank) {
19679 var default_text = '-- SELECT --';
19681 if(this.placeholder.length){
19682 default_text = this.placeholder;
19685 if(this.emptyTitle.length){
19686 default_text += ' - ' + this.emptyTitle + ' -';
19689 var opt = this.inputEl().createChild({
19692 html : default_text
19696 o[this.valueField] = 0;
19697 o[this.displayField] = default_text;
19699 this.ios_options.push({
19706 this.store.data.each(function(d, rowIndex){
19710 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19711 html = d.data[this.displayField];
19716 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19717 value = d.data[this.valueField];
19726 if(this.value == d.data[this.valueField]){
19727 option['selected'] = true;
19730 var opt = this.inputEl().createChild(option);
19732 this.ios_options.push({
19739 this.inputEl().on('change', function(){
19740 this.fireEvent('select', this);
19745 clearIOSView: function()
19747 this.inputEl().dom.innerHTML = '';
19749 this.ios_options = [];
19752 setIOSValue: function(v)
19756 if(!this.ios_options){
19760 Roo.each(this.ios_options, function(opts){
19762 opts.el.dom.removeAttribute('selected');
19764 if(opts.data[this.valueField] != v){
19768 opts.el.dom.setAttribute('selected', true);
19774 * @cfg {Boolean} grow
19778 * @cfg {Number} growMin
19782 * @cfg {Number} growMax
19791 Roo.apply(Roo.bootstrap.form.ComboBox, {
19795 cls: 'modal-header',
19817 cls: 'list-group-item',
19821 cls: 'roo-combobox-list-group-item-value'
19825 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19839 listItemCheckbox : {
19841 cls: 'list-group-item',
19845 cls: 'roo-combobox-list-group-item-value'
19849 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19865 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19870 cls: 'modal-footer',
19878 cls: 'col-xs-6 text-left',
19881 cls: 'btn btn-danger roo-touch-view-cancel',
19887 cls: 'col-xs-6 text-right',
19890 cls: 'btn btn-success roo-touch-view-ok',
19901 Roo.apply(Roo.bootstrap.form.ComboBox, {
19903 touchViewTemplate : {
19905 cls: 'modal fade roo-combobox-touch-view',
19909 cls: 'modal-dialog',
19910 style : 'position:fixed', // we have to fix position....
19914 cls: 'modal-content',
19916 Roo.bootstrap.form.ComboBox.header,
19917 Roo.bootstrap.form.ComboBox.body,
19918 Roo.bootstrap.form.ComboBox.footer
19927 * Ext JS Library 1.1.1
19928 * Copyright(c) 2006-2007, Ext JS, LLC.
19930 * Originally Released Under LGPL - original licence link has changed is not relivant.
19933 * <script type="text/javascript">
19938 * @extends Roo.util.Observable
19939 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19940 * This class also supports single and multi selection modes. <br>
19941 * Create a data model bound view:
19943 var store = new Roo.data.Store(...);
19945 var view = new Roo.View({
19947 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19949 singleSelect: true,
19950 selectedClass: "ydataview-selected",
19954 // listen for node click?
19955 view.on("click", function(vw, index, node, e){
19956 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19960 dataModel.load("foobar.xml");
19962 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19964 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19965 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19967 * Note: old style constructor is still suported (container, template, config)
19970 * Create a new View
19971 * @param {Object} config The config object
19974 Roo.View = function(config, depreciated_tpl, depreciated_config){
19976 this.parent = false;
19978 if (typeof(depreciated_tpl) == 'undefined') {
19979 // new way.. - universal constructor.
19980 Roo.apply(this, config);
19981 this.el = Roo.get(this.el);
19984 this.el = Roo.get(config);
19985 this.tpl = depreciated_tpl;
19986 Roo.apply(this, depreciated_config);
19988 this.wrapEl = this.el.wrap().wrap();
19989 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19992 if(typeof(this.tpl) == "string"){
19993 this.tpl = new Roo.Template(this.tpl);
19995 // support xtype ctors..
19996 this.tpl = new Roo.factory(this.tpl, Roo);
20000 this.tpl.compile();
20005 * @event beforeclick
20006 * Fires before a click is processed. Returns false to cancel the default action.
20007 * @param {Roo.View} this
20008 * @param {Number} index The index of the target node
20009 * @param {HTMLElement} node The target node
20010 * @param {Roo.EventObject} e The raw event object
20012 "beforeclick" : true,
20015 * Fires when a template node is clicked.
20016 * @param {Roo.View} this
20017 * @param {Number} index The index of the target node
20018 * @param {HTMLElement} node The target node
20019 * @param {Roo.EventObject} e The raw event object
20024 * Fires when a template node is double clicked.
20025 * @param {Roo.View} this
20026 * @param {Number} index The index of the target node
20027 * @param {HTMLElement} node The target node
20028 * @param {Roo.EventObject} e The raw event object
20032 * @event contextmenu
20033 * Fires when a template node is right clicked.
20034 * @param {Roo.View} this
20035 * @param {Number} index The index of the target node
20036 * @param {HTMLElement} node The target node
20037 * @param {Roo.EventObject} e The raw event object
20039 "contextmenu" : true,
20041 * @event selectionchange
20042 * Fires when the selected nodes change.
20043 * @param {Roo.View} this
20044 * @param {Array} selections Array of the selected nodes
20046 "selectionchange" : true,
20049 * @event beforeselect
20050 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20051 * @param {Roo.View} this
20052 * @param {HTMLElement} node The node to be selected
20053 * @param {Array} selections Array of currently selected nodes
20055 "beforeselect" : true,
20057 * @event preparedata
20058 * Fires on every row to render, to allow you to change the data.
20059 * @param {Roo.View} this
20060 * @param {Object} data to be rendered (change this)
20062 "preparedata" : true
20070 "click": this.onClick,
20071 "dblclick": this.onDblClick,
20072 "contextmenu": this.onContextMenu,
20076 this.selections = [];
20078 this.cmp = new Roo.CompositeElementLite([]);
20080 this.store = Roo.factory(this.store, Roo.data);
20081 this.setStore(this.store, true);
20084 if ( this.footer && this.footer.xtype) {
20086 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20088 this.footer.dataSource = this.store;
20089 this.footer.container = fctr;
20090 this.footer = Roo.factory(this.footer, Roo);
20091 fctr.insertFirst(this.el);
20093 // this is a bit insane - as the paging toolbar seems to detach the el..
20094 // dom.parentNode.parentNode.parentNode
20095 // they get detached?
20099 Roo.View.superclass.constructor.call(this);
20104 Roo.extend(Roo.View, Roo.util.Observable, {
20107 * @cfg {Roo.data.Store} store Data store to load data from.
20112 * @cfg {String|Roo.Element} el The container element.
20117 * @cfg {String|Roo.Template} tpl The template used by this View
20121 * @cfg {String} dataName the named area of the template to use as the data area
20122 * Works with domtemplates roo-name="name"
20126 * @cfg {String} selectedClass The css class to add to selected nodes
20128 selectedClass : "x-view-selected",
20130 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20135 * @cfg {String} text to display on mask (default Loading)
20139 * @cfg {Boolean} multiSelect Allow multiple selection
20141 multiSelect : false,
20143 * @cfg {Boolean} singleSelect Allow single selection
20145 singleSelect: false,
20148 * @cfg {Boolean} toggleSelect - selecting
20150 toggleSelect : false,
20153 * @cfg {Boolean} tickable - selecting
20158 * Returns the element this view is bound to.
20159 * @return {Roo.Element}
20161 getEl : function(){
20162 return this.wrapEl;
20168 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20170 refresh : function(){
20171 //Roo.log('refresh');
20174 // if we are using something like 'domtemplate', then
20175 // the what gets used is:
20176 // t.applySubtemplate(NAME, data, wrapping data..)
20177 // the outer template then get' applied with
20178 // the store 'extra data'
20179 // and the body get's added to the
20180 // roo-name="data" node?
20181 // <span class='roo-tpl-{name}'></span> ?????
20185 this.clearSelections();
20186 this.el.update("");
20188 var records = this.store.getRange();
20189 if(records.length < 1) {
20191 // is this valid?? = should it render a template??
20193 this.el.update(this.emptyText);
20197 if (this.dataName) {
20198 this.el.update(t.apply(this.store.meta)); //????
20199 el = this.el.child('.roo-tpl-' + this.dataName);
20202 for(var i = 0, len = records.length; i < len; i++){
20203 var data = this.prepareData(records[i].data, i, records[i]);
20204 this.fireEvent("preparedata", this, data, i, records[i]);
20206 var d = Roo.apply({}, data);
20209 Roo.apply(d, {'roo-id' : Roo.id()});
20213 Roo.each(this.parent.item, function(item){
20214 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20217 Roo.apply(d, {'roo-data-checked' : 'checked'});
20221 html[html.length] = Roo.util.Format.trim(
20223 t.applySubtemplate(this.dataName, d, this.store.meta) :
20230 el.update(html.join(""));
20231 this.nodes = el.dom.childNodes;
20232 this.updateIndexes(0);
20237 * Function to override to reformat the data that is sent to
20238 * the template for each node.
20239 * DEPRICATED - use the preparedata event handler.
20240 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20241 * a JSON object for an UpdateManager bound view).
20243 prepareData : function(data, index, record)
20245 this.fireEvent("preparedata", this, data, index, record);
20249 onUpdate : function(ds, record){
20250 // Roo.log('on update');
20251 this.clearSelections();
20252 var index = this.store.indexOf(record);
20253 var n = this.nodes[index];
20254 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20255 n.parentNode.removeChild(n);
20256 this.updateIndexes(index, index);
20262 onAdd : function(ds, records, index)
20264 //Roo.log(['on Add', ds, records, index] );
20265 this.clearSelections();
20266 if(this.nodes.length == 0){
20270 var n = this.nodes[index];
20271 for(var i = 0, len = records.length; i < len; i++){
20272 var d = this.prepareData(records[i].data, i, records[i]);
20274 this.tpl.insertBefore(n, d);
20277 this.tpl.append(this.el, d);
20280 this.updateIndexes(index);
20283 onRemove : function(ds, record, index){
20284 // Roo.log('onRemove');
20285 this.clearSelections();
20286 var el = this.dataName ?
20287 this.el.child('.roo-tpl-' + this.dataName) :
20290 el.dom.removeChild(this.nodes[index]);
20291 this.updateIndexes(index);
20295 * Refresh an individual node.
20296 * @param {Number} index
20298 refreshNode : function(index){
20299 this.onUpdate(this.store, this.store.getAt(index));
20302 updateIndexes : function(startIndex, endIndex){
20303 var ns = this.nodes;
20304 startIndex = startIndex || 0;
20305 endIndex = endIndex || ns.length - 1;
20306 for(var i = startIndex; i <= endIndex; i++){
20307 ns[i].nodeIndex = i;
20312 * Changes the data store this view uses and refresh the view.
20313 * @param {Store} store
20315 setStore : function(store, initial){
20316 if(!initial && this.store){
20317 this.store.un("datachanged", this.refresh);
20318 this.store.un("add", this.onAdd);
20319 this.store.un("remove", this.onRemove);
20320 this.store.un("update", this.onUpdate);
20321 this.store.un("clear", this.refresh);
20322 this.store.un("beforeload", this.onBeforeLoad);
20323 this.store.un("load", this.onLoad);
20324 this.store.un("loadexception", this.onLoad);
20328 store.on("datachanged", this.refresh, this);
20329 store.on("add", this.onAdd, this);
20330 store.on("remove", this.onRemove, this);
20331 store.on("update", this.onUpdate, this);
20332 store.on("clear", this.refresh, this);
20333 store.on("beforeload", this.onBeforeLoad, this);
20334 store.on("load", this.onLoad, this);
20335 store.on("loadexception", this.onLoad, this);
20343 * onbeforeLoad - masks the loading area.
20346 onBeforeLoad : function(store,opts)
20348 //Roo.log('onBeforeLoad');
20350 this.el.update("");
20352 this.el.mask(this.mask ? this.mask : "Loading" );
20354 onLoad : function ()
20361 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20362 * @param {HTMLElement} node
20363 * @return {HTMLElement} The template node
20365 findItemFromChild : function(node){
20366 var el = this.dataName ?
20367 this.el.child('.roo-tpl-' + this.dataName,true) :
20370 if(!node || node.parentNode == el){
20373 var p = node.parentNode;
20374 while(p && p != el){
20375 if(p.parentNode == el){
20384 onClick : function(e){
20385 var item = this.findItemFromChild(e.getTarget());
20387 var index = this.indexOf(item);
20388 if(this.onItemClick(item, index, e) !== false){
20389 this.fireEvent("click", this, index, item, e);
20392 this.clearSelections();
20397 onContextMenu : function(e){
20398 var item = this.findItemFromChild(e.getTarget());
20400 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20405 onDblClick : function(e){
20406 var item = this.findItemFromChild(e.getTarget());
20408 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20412 onItemClick : function(item, index, e)
20414 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20417 if (this.toggleSelect) {
20418 var m = this.isSelected(item) ? 'unselect' : 'select';
20421 _t[m](item, true, false);
20424 if(this.multiSelect || this.singleSelect){
20425 if(this.multiSelect && e.shiftKey && this.lastSelection){
20426 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20428 this.select(item, this.multiSelect && e.ctrlKey);
20429 this.lastSelection = item;
20432 if(!this.tickable){
20433 e.preventDefault();
20441 * Get the number of selected nodes.
20444 getSelectionCount : function(){
20445 return this.selections.length;
20449 * Get the currently selected nodes.
20450 * @return {Array} An array of HTMLElements
20452 getSelectedNodes : function(){
20453 return this.selections;
20457 * Get the indexes of the selected nodes.
20460 getSelectedIndexes : function(){
20461 var indexes = [], s = this.selections;
20462 for(var i = 0, len = s.length; i < len; i++){
20463 indexes.push(s[i].nodeIndex);
20469 * Clear all selections
20470 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20472 clearSelections : function(suppressEvent){
20473 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20474 this.cmp.elements = this.selections;
20475 this.cmp.removeClass(this.selectedClass);
20476 this.selections = [];
20477 if(!suppressEvent){
20478 this.fireEvent("selectionchange", this, this.selections);
20484 * Returns true if the passed node is selected
20485 * @param {HTMLElement/Number} node The node or node index
20486 * @return {Boolean}
20488 isSelected : function(node){
20489 var s = this.selections;
20493 node = this.getNode(node);
20494 return s.indexOf(node) !== -1;
20499 * @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
20500 * @param {Boolean} keepExisting (optional) true to keep existing selections
20501 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20503 select : function(nodeInfo, keepExisting, suppressEvent){
20504 if(nodeInfo instanceof Array){
20506 this.clearSelections(true);
20508 for(var i = 0, len = nodeInfo.length; i < len; i++){
20509 this.select(nodeInfo[i], true, true);
20513 var node = this.getNode(nodeInfo);
20514 if(!node || this.isSelected(node)){
20515 return; // already selected.
20518 this.clearSelections(true);
20521 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20522 Roo.fly(node).addClass(this.selectedClass);
20523 this.selections.push(node);
20524 if(!suppressEvent){
20525 this.fireEvent("selectionchange", this, this.selections);
20533 * @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
20534 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20535 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20537 unselect : function(nodeInfo, keepExisting, suppressEvent)
20539 if(nodeInfo instanceof Array){
20540 Roo.each(this.selections, function(s) {
20541 this.unselect(s, nodeInfo);
20545 var node = this.getNode(nodeInfo);
20546 if(!node || !this.isSelected(node)){
20547 //Roo.log("not selected");
20548 return; // not selected.
20552 Roo.each(this.selections, function(s) {
20554 Roo.fly(node).removeClass(this.selectedClass);
20561 this.selections= ns;
20562 this.fireEvent("selectionchange", this, this.selections);
20566 * Gets a template node.
20567 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20568 * @return {HTMLElement} The node or null if it wasn't found
20570 getNode : function(nodeInfo){
20571 if(typeof nodeInfo == "string"){
20572 return document.getElementById(nodeInfo);
20573 }else if(typeof nodeInfo == "number"){
20574 return this.nodes[nodeInfo];
20580 * Gets a range template nodes.
20581 * @param {Number} startIndex
20582 * @param {Number} endIndex
20583 * @return {Array} An array of nodes
20585 getNodes : function(start, end){
20586 var ns = this.nodes;
20587 start = start || 0;
20588 end = typeof end == "undefined" ? ns.length - 1 : end;
20591 for(var i = start; i <= end; i++){
20595 for(var i = start; i >= end; i--){
20603 * Finds the index of the passed node
20604 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20605 * @return {Number} The index of the node or -1
20607 indexOf : function(node){
20608 node = this.getNode(node);
20609 if(typeof node.nodeIndex == "number"){
20610 return node.nodeIndex;
20612 var ns = this.nodes;
20613 for(var i = 0, len = ns.length; i < len; i++){
20624 * based on jquery fullcalendar
20628 Roo.bootstrap = Roo.bootstrap || {};
20630 * @class Roo.bootstrap.Calendar
20631 * @extends Roo.bootstrap.Component
20632 * Bootstrap Calendar class
20633 * @cfg {Boolean} loadMask (true|false) default false
20634 * @cfg {Object} header generate the user specific header of the calendar, default false
20637 * Create a new Container
20638 * @param {Object} config The config object
20643 Roo.bootstrap.Calendar = function(config){
20644 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20648 * Fires when a date is selected
20649 * @param {DatePicker} this
20650 * @param {Date} date The selected date
20654 * @event monthchange
20655 * Fires when the displayed month changes
20656 * @param {DatePicker} this
20657 * @param {Date} date The selected month
20659 'monthchange': true,
20661 * @event evententer
20662 * Fires when mouse over an event
20663 * @param {Calendar} this
20664 * @param {event} Event
20666 'evententer': true,
20668 * @event eventleave
20669 * Fires when the mouse leaves an
20670 * @param {Calendar} this
20673 'eventleave': true,
20675 * @event eventclick
20676 * Fires when the mouse click an
20677 * @param {Calendar} this
20686 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20689 * @cfg {Roo.data.Store} store
20690 * The data source for the calendar
20694 * @cfg {Number} startDay
20695 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20703 getAutoCreate : function(){
20706 var fc_button = function(name, corner, style, content ) {
20707 return Roo.apply({},{
20709 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20711 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20714 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20725 style : 'width:100%',
20732 cls : 'fc-header-left',
20734 fc_button('prev', 'left', 'arrow', '‹' ),
20735 fc_button('next', 'right', 'arrow', '›' ),
20736 { tag: 'span', cls: 'fc-header-space' },
20737 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20745 cls : 'fc-header-center',
20749 cls: 'fc-header-title',
20752 html : 'month / year'
20760 cls : 'fc-header-right',
20762 /* fc_button('month', 'left', '', 'month' ),
20763 fc_button('week', '', '', 'week' ),
20764 fc_button('day', 'right', '', 'day' )
20776 header = this.header;
20779 var cal_heads = function() {
20781 // fixme - handle this.
20783 for (var i =0; i < Date.dayNames.length; i++) {
20784 var d = Date.dayNames[i];
20787 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20788 html : d.substring(0,3)
20792 ret[0].cls += ' fc-first';
20793 ret[6].cls += ' fc-last';
20796 var cal_cell = function(n) {
20799 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20804 cls: 'fc-day-number',
20808 cls: 'fc-day-content',
20812 style: 'position: relative;' // height: 17px;
20824 var cal_rows = function() {
20827 for (var r = 0; r < 6; r++) {
20834 for (var i =0; i < Date.dayNames.length; i++) {
20835 var d = Date.dayNames[i];
20836 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20839 row.cn[0].cls+=' fc-first';
20840 row.cn[0].cn[0].style = 'min-height:90px';
20841 row.cn[6].cls+=' fc-last';
20845 ret[0].cls += ' fc-first';
20846 ret[4].cls += ' fc-prev-last';
20847 ret[5].cls += ' fc-last';
20854 cls: 'fc-border-separate',
20855 style : 'width:100%',
20863 cls : 'fc-first fc-last',
20881 cls : 'fc-content',
20882 style : "position: relative;",
20885 cls : 'fc-view fc-view-month fc-grid',
20886 style : 'position: relative',
20887 unselectable : 'on',
20890 cls : 'fc-event-container',
20891 style : 'position:absolute;z-index:8;top:0;left:0;'
20909 initEvents : function()
20912 throw "can not find store for calendar";
20918 style: "text-align:center",
20922 style: "background-color:white;width:50%;margin:250 auto",
20926 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20937 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20939 var size = this.el.select('.fc-content', true).first().getSize();
20940 this.maskEl.setSize(size.width, size.height);
20941 this.maskEl.enableDisplayMode("block");
20942 if(!this.loadMask){
20943 this.maskEl.hide();
20946 this.store = Roo.factory(this.store, Roo.data);
20947 this.store.on('load', this.onLoad, this);
20948 this.store.on('beforeload', this.onBeforeLoad, this);
20952 this.cells = this.el.select('.fc-day',true);
20953 //Roo.log(this.cells);
20954 this.textNodes = this.el.query('.fc-day-number');
20955 this.cells.addClassOnOver('fc-state-hover');
20957 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20958 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20959 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20960 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20962 this.on('monthchange', this.onMonthChange, this);
20964 this.update(new Date().clearTime());
20967 resize : function() {
20968 var sz = this.el.getSize();
20970 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20971 this.el.select('.fc-day-content div',true).setHeight(34);
20976 showPrevMonth : function(e){
20977 this.update(this.activeDate.add("mo", -1));
20979 showToday : function(e){
20980 this.update(new Date().clearTime());
20983 showNextMonth : function(e){
20984 this.update(this.activeDate.add("mo", 1));
20988 showPrevYear : function(){
20989 this.update(this.activeDate.add("y", -1));
20993 showNextYear : function(){
20994 this.update(this.activeDate.add("y", 1));
20999 update : function(date)
21001 var vd = this.activeDate;
21002 this.activeDate = date;
21003 // if(vd && this.el){
21004 // var t = date.getTime();
21005 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21006 // Roo.log('using add remove');
21008 // this.fireEvent('monthchange', this, date);
21010 // this.cells.removeClass("fc-state-highlight");
21011 // this.cells.each(function(c){
21012 // if(c.dateValue == t){
21013 // c.addClass("fc-state-highlight");
21014 // setTimeout(function(){
21015 // try{c.dom.firstChild.focus();}catch(e){}
21025 var days = date.getDaysInMonth();
21027 var firstOfMonth = date.getFirstDateOfMonth();
21028 var startingPos = firstOfMonth.getDay()-this.startDay;
21030 if(startingPos < this.startDay){
21034 var pm = date.add(Date.MONTH, -1);
21035 var prevStart = pm.getDaysInMonth()-startingPos;
21037 this.cells = this.el.select('.fc-day',true);
21038 this.textNodes = this.el.query('.fc-day-number');
21039 this.cells.addClassOnOver('fc-state-hover');
21041 var cells = this.cells.elements;
21042 var textEls = this.textNodes;
21044 Roo.each(cells, function(cell){
21045 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21048 days += startingPos;
21050 // convert everything to numbers so it's fast
21051 var day = 86400000;
21052 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21055 //Roo.log(prevStart);
21057 var today = new Date().clearTime().getTime();
21058 var sel = date.clearTime().getTime();
21059 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21060 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21061 var ddMatch = this.disabledDatesRE;
21062 var ddText = this.disabledDatesText;
21063 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21064 var ddaysText = this.disabledDaysText;
21065 var format = this.format;
21067 var setCellClass = function(cal, cell){
21071 //Roo.log('set Cell Class');
21073 var t = d.getTime();
21077 cell.dateValue = t;
21079 cell.className += " fc-today";
21080 cell.className += " fc-state-highlight";
21081 cell.title = cal.todayText;
21084 // disable highlight in other month..
21085 //cell.className += " fc-state-highlight";
21090 cell.className = " fc-state-disabled";
21091 cell.title = cal.minText;
21095 cell.className = " fc-state-disabled";
21096 cell.title = cal.maxText;
21100 if(ddays.indexOf(d.getDay()) != -1){
21101 cell.title = ddaysText;
21102 cell.className = " fc-state-disabled";
21105 if(ddMatch && format){
21106 var fvalue = d.dateFormat(format);
21107 if(ddMatch.test(fvalue)){
21108 cell.title = ddText.replace("%0", fvalue);
21109 cell.className = " fc-state-disabled";
21113 if (!cell.initialClassName) {
21114 cell.initialClassName = cell.dom.className;
21117 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21122 for(; i < startingPos; i++) {
21123 textEls[i].innerHTML = (++prevStart);
21124 d.setDate(d.getDate()+1);
21126 cells[i].className = "fc-past fc-other-month";
21127 setCellClass(this, cells[i]);
21132 for(; i < days; i++){
21133 intDay = i - startingPos + 1;
21134 textEls[i].innerHTML = (intDay);
21135 d.setDate(d.getDate()+1);
21137 cells[i].className = ''; // "x-date-active";
21138 setCellClass(this, cells[i]);
21142 for(; i < 42; i++) {
21143 textEls[i].innerHTML = (++extraDays);
21144 d.setDate(d.getDate()+1);
21146 cells[i].className = "fc-future fc-other-month";
21147 setCellClass(this, cells[i]);
21150 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21152 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21154 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21155 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21157 if(totalRows != 6){
21158 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21159 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21162 this.fireEvent('monthchange', this, date);
21166 if(!this.internalRender){
21167 var main = this.el.dom.firstChild;
21168 var w = main.offsetWidth;
21169 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21170 Roo.fly(main).setWidth(w);
21171 this.internalRender = true;
21172 // opera does not respect the auto grow header center column
21173 // then, after it gets a width opera refuses to recalculate
21174 // without a second pass
21175 if(Roo.isOpera && !this.secondPass){
21176 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21177 this.secondPass = true;
21178 this.update.defer(10, this, [date]);
21185 findCell : function(dt) {
21186 dt = dt.clearTime().getTime();
21188 this.cells.each(function(c){
21189 //Roo.log("check " +c.dateValue + '?=' + dt);
21190 if(c.dateValue == dt){
21200 findCells : function(ev) {
21201 var s = ev.start.clone().clearTime().getTime();
21203 var e= ev.end.clone().clearTime().getTime();
21206 this.cells.each(function(c){
21207 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21209 if(c.dateValue > e){
21212 if(c.dateValue < s){
21221 // findBestRow: function(cells)
21225 // for (var i =0 ; i < cells.length;i++) {
21226 // ret = Math.max(cells[i].rows || 0,ret);
21233 addItem : function(ev)
21235 // look for vertical location slot in
21236 var cells = this.findCells(ev);
21238 // ev.row = this.findBestRow(cells);
21240 // work out the location.
21244 for(var i =0; i < cells.length; i++) {
21246 cells[i].row = cells[0].row;
21249 cells[i].row = cells[i].row + 1;
21259 if (crow.start.getY() == cells[i].getY()) {
21261 crow.end = cells[i];
21278 cells[0].events.push(ev);
21280 this.calevents.push(ev);
21283 clearEvents: function() {
21285 if(!this.calevents){
21289 Roo.each(this.cells.elements, function(c){
21295 Roo.each(this.calevents, function(e) {
21296 Roo.each(e.els, function(el) {
21297 el.un('mouseenter' ,this.onEventEnter, this);
21298 el.un('mouseleave' ,this.onEventLeave, this);
21303 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21309 renderEvents: function()
21313 this.cells.each(function(c) {
21322 if(c.row != c.events.length){
21323 r = 4 - (4 - (c.row - c.events.length));
21326 c.events = ev.slice(0, r);
21327 c.more = ev.slice(r);
21329 if(c.more.length && c.more.length == 1){
21330 c.events.push(c.more.pop());
21333 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21337 this.cells.each(function(c) {
21339 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21342 for (var e = 0; e < c.events.length; e++){
21343 var ev = c.events[e];
21344 var rows = ev.rows;
21346 for(var i = 0; i < rows.length; i++) {
21348 // how many rows should it span..
21351 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21352 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21354 unselectable : "on",
21357 cls: 'fc-event-inner',
21361 // cls: 'fc-event-time',
21362 // html : cells.length > 1 ? '' : ev.time
21366 cls: 'fc-event-title',
21367 html : String.format('{0}', ev.title)
21374 cls: 'ui-resizable-handle ui-resizable-e',
21375 html : '  '
21382 cfg.cls += ' fc-event-start';
21384 if ((i+1) == rows.length) {
21385 cfg.cls += ' fc-event-end';
21388 var ctr = _this.el.select('.fc-event-container',true).first();
21389 var cg = ctr.createChild(cfg);
21391 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21392 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21394 var r = (c.more.length) ? 1 : 0;
21395 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21396 cg.setWidth(ebox.right - sbox.x -2);
21398 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21399 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21400 cg.on('click', _this.onEventClick, _this, ev);
21411 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21412 style : 'position: absolute',
21413 unselectable : "on",
21416 cls: 'fc-event-inner',
21420 cls: 'fc-event-title',
21428 cls: 'ui-resizable-handle ui-resizable-e',
21429 html : '  '
21435 var ctr = _this.el.select('.fc-event-container',true).first();
21436 var cg = ctr.createChild(cfg);
21438 var sbox = c.select('.fc-day-content',true).first().getBox();
21439 var ebox = c.select('.fc-day-content',true).first().getBox();
21441 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21442 cg.setWidth(ebox.right - sbox.x -2);
21444 cg.on('click', _this.onMoreEventClick, _this, c.more);
21454 onEventEnter: function (e, el,event,d) {
21455 this.fireEvent('evententer', this, el, event);
21458 onEventLeave: function (e, el,event,d) {
21459 this.fireEvent('eventleave', this, el, event);
21462 onEventClick: function (e, el,event,d) {
21463 this.fireEvent('eventclick', this, el, event);
21466 onMonthChange: function () {
21470 onMoreEventClick: function(e, el, more)
21474 this.calpopover.placement = 'right';
21475 this.calpopover.setTitle('More');
21477 this.calpopover.setContent('');
21479 var ctr = this.calpopover.el.select('.popover-content', true).first();
21481 Roo.each(more, function(m){
21483 cls : 'fc-event-hori fc-event-draggable',
21486 var cg = ctr.createChild(cfg);
21488 cg.on('click', _this.onEventClick, _this, m);
21491 this.calpopover.show(el);
21496 onLoad: function ()
21498 this.calevents = [];
21501 if(this.store.getCount() > 0){
21502 this.store.data.each(function(d){
21505 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21506 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21507 time : d.data.start_time,
21508 title : d.data.title,
21509 description : d.data.description,
21510 venue : d.data.venue
21515 this.renderEvents();
21517 if(this.calevents.length && this.loadMask){
21518 this.maskEl.hide();
21522 onBeforeLoad: function()
21524 this.clearEvents();
21526 this.maskEl.show();
21540 * @class Roo.bootstrap.Popover
21541 * @extends Roo.bootstrap.Component
21542 * @parent none builder
21543 * @children Roo.bootstrap.Component
21544 * Bootstrap Popover class
21545 * @cfg {String} html contents of the popover (or false to use children..)
21546 * @cfg {String} title of popover (or false to hide)
21547 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21548 * @cfg {String} trigger click || hover (or false to trigger manually)
21549 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21550 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21551 * - if false and it has a 'parent' then it will be automatically added to that element
21552 * - if string - Roo.get will be called
21553 * @cfg {Number} delay - delay before showing
21556 * Create a new Popover
21557 * @param {Object} config The config object
21560 Roo.bootstrap.Popover = function(config){
21561 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21567 * After the popover show
21569 * @param {Roo.bootstrap.Popover} this
21574 * After the popover hide
21576 * @param {Roo.bootstrap.Popover} this
21582 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21587 placement : 'right',
21588 trigger : 'hover', // hover
21594 can_build_overlaid : false,
21596 maskEl : false, // the mask element
21599 alignEl : false, // when show is called with an element - this get's stored.
21601 getChildContainer : function()
21603 return this.contentEl;
21606 getPopoverHeader : function()
21608 this.title = true; // flag not to hide it..
21609 this.headerEl.addClass('p-0');
21610 return this.headerEl
21614 getAutoCreate : function(){
21617 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21618 style: 'display:block',
21624 cls : 'popover-inner ',
21628 cls: 'popover-title popover-header',
21629 html : this.title === false ? '' : this.title
21632 cls : 'popover-content popover-body ' + (this.cls || ''),
21633 html : this.html || ''
21644 * @param {string} the title
21646 setTitle: function(str)
21650 this.headerEl.dom.innerHTML = str;
21655 * @param {string} the body content
21657 setContent: function(str)
21660 if (this.contentEl) {
21661 this.contentEl.dom.innerHTML = str;
21665 // as it get's added to the bottom of the page.
21666 onRender : function(ct, position)
21668 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21673 var cfg = Roo.apply({}, this.getAutoCreate());
21677 cfg.cls += ' ' + this.cls;
21680 cfg.style = this.style;
21682 //Roo.log("adding to ");
21683 this.el = Roo.get(document.body).createChild(cfg, position);
21684 // Roo.log(this.el);
21687 this.contentEl = this.el.select('.popover-content',true).first();
21688 this.headerEl = this.el.select('.popover-title',true).first();
21691 if(typeof(this.items) != 'undefined'){
21692 var items = this.items;
21695 for(var i =0;i < items.length;i++) {
21696 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21700 this.items = nitems;
21702 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21703 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21710 resizeMask : function()
21712 this.maskEl.setSize(
21713 Roo.lib.Dom.getViewWidth(true),
21714 Roo.lib.Dom.getViewHeight(true)
21718 initEvents : function()
21722 Roo.bootstrap.Popover.register(this);
21725 this.arrowEl = this.el.select('.arrow',true).first();
21726 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21727 this.el.enableDisplayMode('block');
21731 if (this.over === false && !this.parent()) {
21734 if (this.triggers === false) {
21739 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21740 var triggers = this.trigger ? this.trigger.split(' ') : [];
21741 Roo.each(triggers, function(trigger) {
21743 if (trigger == 'click') {
21744 on_el.on('click', this.toggle, this);
21745 } else if (trigger != 'manual') {
21746 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21747 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21749 on_el.on(eventIn ,this.enter, this);
21750 on_el.on(eventOut, this.leave, this);
21760 toggle : function () {
21761 this.hoverState == 'in' ? this.leave() : this.enter();
21764 enter : function () {
21766 clearTimeout(this.timeout);
21768 this.hoverState = 'in';
21770 if (!this.delay || !this.delay.show) {
21775 this.timeout = setTimeout(function () {
21776 if (_t.hoverState == 'in') {
21779 }, this.delay.show)
21782 leave : function() {
21783 clearTimeout(this.timeout);
21785 this.hoverState = 'out';
21787 if (!this.delay || !this.delay.hide) {
21792 this.timeout = setTimeout(function () {
21793 if (_t.hoverState == 'out') {
21796 }, this.delay.hide)
21800 * update the position of the dialog
21801 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21806 doAlign : function()
21809 if (this.alignEl) {
21810 this.updatePosition(this.placement, true);
21813 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21814 var es = this.el.getSize();
21815 var x = Roo.lib.Dom.getViewWidth()/2;
21816 var y = Roo.lib.Dom.getViewHeight()/2;
21817 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21829 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21830 * @param {string} (left|right|top|bottom) position
21832 show : function (on_el, placement)
21834 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21835 on_el = on_el || false; // default to false
21838 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21839 on_el = this.parent().el;
21840 } else if (this.over) {
21841 on_el = Roo.get(this.over);
21846 this.alignEl = Roo.get( on_el );
21849 this.render(document.body);
21855 if (this.title === false) {
21856 this.headerEl.hide();
21861 this.el.dom.style.display = 'block';
21865 //var arrow = this.el.select('.arrow',true).first();
21866 //arrow.set(align[2],
21868 this.el.addClass('in');
21872 this.hoverState = 'in';
21875 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21876 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21877 this.maskEl.dom.style.display = 'block';
21878 this.maskEl.addClass('show');
21880 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21882 this.fireEvent('show', this);
21886 * fire this manually after loading a grid in the table for example
21887 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21888 * @param {Boolean} try and move it if we cant get right position.
21890 updatePosition : function(placement, try_move)
21892 // allow for calling with no parameters
21893 placement = placement ? placement : this.placement;
21894 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21896 this.el.removeClass([
21897 'fade','top','bottom', 'left', 'right','in',
21898 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21900 this.el.addClass(placement + ' bs-popover-' + placement);
21902 if (!this.alignEl ) {
21906 switch (placement) {
21908 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21909 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21910 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21911 //normal display... or moved up/down.
21912 this.el.setXY(offset);
21913 var xy = this.alignEl.getAnchorXY('tr', false);
21915 this.arrowEl.setXY(xy);
21918 // continue through...
21919 return this.updatePosition('left', false);
21923 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21924 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21925 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21926 //normal display... or moved up/down.
21927 this.el.setXY(offset);
21928 var xy = this.alignEl.getAnchorXY('tl', false);
21929 xy[0]-=10;xy[1]+=5; // << fix me
21930 this.arrowEl.setXY(xy);
21934 return this.updatePosition('right', false);
21937 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21938 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21939 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21940 //normal display... or moved up/down.
21941 this.el.setXY(offset);
21942 var xy = this.alignEl.getAnchorXY('t', false);
21943 xy[1]-=10; // << fix me
21944 this.arrowEl.setXY(xy);
21948 return this.updatePosition('bottom', false);
21951 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21952 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21953 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21954 //normal display... or moved up/down.
21955 this.el.setXY(offset);
21956 var xy = this.alignEl.getAnchorXY('b', false);
21957 xy[1]+=2; // << fix me
21958 this.arrowEl.setXY(xy);
21962 return this.updatePosition('top', false);
21973 this.el.setXY([0,0]);
21974 this.el.removeClass('in');
21976 this.hoverState = null;
21977 this.maskEl.hide(); // always..
21978 this.fireEvent('hide', this);
21984 Roo.apply(Roo.bootstrap.Popover, {
21987 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21988 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21989 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21990 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21995 clickHander : false,
21999 onMouseDown : function(e)
22001 if (this.popups.length && !e.getTarget(".roo-popover")) {
22002 /// what is nothing is showing..
22011 register : function(popup)
22013 if (!Roo.bootstrap.Popover.clickHandler) {
22014 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22016 // hide other popups.
22017 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22018 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22019 this.hideAll(); //<< why?
22020 //this.popups.push(popup);
22022 hideAll : function()
22024 this.popups.forEach(function(p) {
22028 onShow : function() {
22029 Roo.bootstrap.Popover.popups.push(this);
22031 onHide : function() {
22032 Roo.bootstrap.Popover.popups.remove(this);
22037 * @class Roo.bootstrap.PopoverNav
22038 * @extends Roo.bootstrap.nav.Simplebar
22039 * @parent Roo.bootstrap.Popover
22040 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22042 * Bootstrap Popover header navigation class
22043 * FIXME? should this go under nav?
22047 * Create a new Popover Header Navigation
22048 * @param {Object} config The config object
22051 Roo.bootstrap.PopoverNav = function(config){
22052 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22055 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22058 container_method : 'getPopoverHeader'
22076 * @class Roo.bootstrap.Progress
22077 * @extends Roo.bootstrap.Component
22078 * @children Roo.bootstrap.ProgressBar
22079 * Bootstrap Progress class
22080 * @cfg {Boolean} striped striped of the progress bar
22081 * @cfg {Boolean} active animated of the progress bar
22085 * Create a new Progress
22086 * @param {Object} config The config object
22089 Roo.bootstrap.Progress = function(config){
22090 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22093 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22098 getAutoCreate : function(){
22106 cfg.cls += ' progress-striped';
22110 cfg.cls += ' active';
22129 * @class Roo.bootstrap.ProgressBar
22130 * @extends Roo.bootstrap.Component
22131 * Bootstrap ProgressBar class
22132 * @cfg {Number} aria_valuenow aria-value now
22133 * @cfg {Number} aria_valuemin aria-value min
22134 * @cfg {Number} aria_valuemax aria-value max
22135 * @cfg {String} label label for the progress bar
22136 * @cfg {String} panel (success | info | warning | danger )
22137 * @cfg {String} role role of the progress bar
22138 * @cfg {String} sr_only text
22142 * Create a new ProgressBar
22143 * @param {Object} config The config object
22146 Roo.bootstrap.ProgressBar = function(config){
22147 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22150 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22154 aria_valuemax : 100,
22160 getAutoCreate : function()
22165 cls: 'progress-bar',
22166 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22178 cfg.role = this.role;
22181 if(this.aria_valuenow){
22182 cfg['aria-valuenow'] = this.aria_valuenow;
22185 if(this.aria_valuemin){
22186 cfg['aria-valuemin'] = this.aria_valuemin;
22189 if(this.aria_valuemax){
22190 cfg['aria-valuemax'] = this.aria_valuemax;
22193 if(this.label && !this.sr_only){
22194 cfg.html = this.label;
22198 cfg.cls += ' progress-bar-' + this.panel;
22204 update : function(aria_valuenow)
22206 this.aria_valuenow = aria_valuenow;
22208 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22216 * @class Roo.bootstrap.TabGroup
22217 * @extends Roo.bootstrap.Column
22218 * @children Roo.bootstrap.TabPanel
22219 * Bootstrap Column class
22220 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22221 * @cfg {Boolean} carousel true to make the group behave like a carousel
22222 * @cfg {Boolean} bullets show bullets for the panels
22223 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22224 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22225 * @cfg {Boolean} showarrow (true|false) show arrow default true
22228 * Create a new TabGroup
22229 * @param {Object} config The config object
22232 Roo.bootstrap.TabGroup = function(config){
22233 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22235 this.navId = Roo.id();
22238 Roo.bootstrap.TabGroup.register(this);
22242 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22245 transition : false,
22250 slideOnTouch : false,
22253 getAutoCreate : function()
22255 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22257 cfg.cls += ' tab-content';
22259 if (this.carousel) {
22260 cfg.cls += ' carousel slide';
22263 cls : 'carousel-inner',
22267 if(this.bullets && !Roo.isTouch){
22270 cls : 'carousel-bullets',
22274 if(this.bullets_cls){
22275 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22282 cfg.cn[0].cn.push(bullets);
22285 if(this.showarrow){
22286 cfg.cn[0].cn.push({
22288 class : 'carousel-arrow',
22292 class : 'carousel-prev',
22296 class : 'fa fa-chevron-left'
22302 class : 'carousel-next',
22306 class : 'fa fa-chevron-right'
22319 initEvents: function()
22321 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22322 // this.el.on("touchstart", this.onTouchStart, this);
22325 if(this.autoslide){
22328 this.slideFn = window.setInterval(function() {
22329 _this.showPanelNext();
22333 if(this.showarrow){
22334 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22335 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22341 // onTouchStart : function(e, el, o)
22343 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22347 // this.showPanelNext();
22351 getChildContainer : function()
22353 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22357 * register a Navigation item
22358 * @param {Roo.bootstrap.nav.Item} the navitem to add
22360 register : function(item)
22362 this.tabs.push( item);
22363 item.navId = this.navId; // not really needed..
22368 getActivePanel : function()
22371 Roo.each(this.tabs, function(t) {
22381 getPanelByName : function(n)
22384 Roo.each(this.tabs, function(t) {
22385 if (t.tabId == n) {
22393 indexOfPanel : function(p)
22396 Roo.each(this.tabs, function(t,i) {
22397 if (t.tabId == p.tabId) {
22406 * show a specific panel
22407 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22408 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22410 showPanel : function (pan)
22412 if(this.transition || typeof(pan) == 'undefined'){
22413 Roo.log("waiting for the transitionend");
22417 if (typeof(pan) == 'number') {
22418 pan = this.tabs[pan];
22421 if (typeof(pan) == 'string') {
22422 pan = this.getPanelByName(pan);
22425 var cur = this.getActivePanel();
22428 Roo.log('pan or acitve pan is undefined');
22432 if (pan.tabId == this.getActivePanel().tabId) {
22436 if (false === cur.fireEvent('beforedeactivate')) {
22440 if(this.bullets > 0 && !Roo.isTouch){
22441 this.setActiveBullet(this.indexOfPanel(pan));
22444 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22446 //class="carousel-item carousel-item-next carousel-item-left"
22448 this.transition = true;
22449 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22450 var lr = dir == 'next' ? 'left' : 'right';
22451 pan.el.addClass(dir); // or prev
22452 pan.el.addClass('carousel-item-' + dir); // or prev
22453 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22454 cur.el.addClass(lr); // or right
22455 pan.el.addClass(lr);
22456 cur.el.addClass('carousel-item-' +lr); // or right
22457 pan.el.addClass('carousel-item-' +lr);
22461 cur.el.on('transitionend', function() {
22462 Roo.log("trans end?");
22464 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22465 pan.setActive(true);
22467 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22468 cur.setActive(false);
22470 _this.transition = false;
22472 }, this, { single: true } );
22477 cur.setActive(false);
22478 pan.setActive(true);
22483 showPanelNext : function()
22485 var i = this.indexOfPanel(this.getActivePanel());
22487 if (i >= this.tabs.length - 1 && !this.autoslide) {
22491 if (i >= this.tabs.length - 1 && this.autoslide) {
22495 this.showPanel(this.tabs[i+1]);
22498 showPanelPrev : function()
22500 var i = this.indexOfPanel(this.getActivePanel());
22502 if (i < 1 && !this.autoslide) {
22506 if (i < 1 && this.autoslide) {
22507 i = this.tabs.length;
22510 this.showPanel(this.tabs[i-1]);
22514 addBullet: function()
22516 if(!this.bullets || Roo.isTouch){
22519 var ctr = this.el.select('.carousel-bullets',true).first();
22520 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22521 var bullet = ctr.createChild({
22522 cls : 'bullet bullet-' + i
22523 },ctr.dom.lastChild);
22528 bullet.on('click', (function(e, el, o, ii, t){
22530 e.preventDefault();
22532 this.showPanel(ii);
22534 if(this.autoslide && this.slideFn){
22535 clearInterval(this.slideFn);
22536 this.slideFn = window.setInterval(function() {
22537 _this.showPanelNext();
22541 }).createDelegate(this, [i, bullet], true));
22546 setActiveBullet : function(i)
22552 Roo.each(this.el.select('.bullet', true).elements, function(el){
22553 el.removeClass('selected');
22556 var bullet = this.el.select('.bullet-' + i, true).first();
22562 bullet.addClass('selected');
22573 Roo.apply(Roo.bootstrap.TabGroup, {
22577 * register a Navigation Group
22578 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22580 register : function(navgrp)
22582 this.groups[navgrp.navId] = navgrp;
22586 * fetch a Navigation Group based on the navigation ID
22587 * if one does not exist , it will get created.
22588 * @param {string} the navgroup to add
22589 * @returns {Roo.bootstrap.nav.Group} the navgroup
22591 get: function(navId) {
22592 if (typeof(this.groups[navId]) == 'undefined') {
22593 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22595 return this.groups[navId] ;
22610 * @class Roo.bootstrap.TabPanel
22611 * @extends Roo.bootstrap.Component
22612 * @children Roo.bootstrap.Component
22613 * Bootstrap TabPanel class
22614 * @cfg {Boolean} active panel active
22615 * @cfg {String} html panel content
22616 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22617 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22618 * @cfg {String} href click to link..
22619 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22623 * Create a new TabPanel
22624 * @param {Object} config The config object
22627 Roo.bootstrap.TabPanel = function(config){
22628 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22632 * Fires when the active status changes
22633 * @param {Roo.bootstrap.TabPanel} this
22634 * @param {Boolean} state the new state
22639 * @event beforedeactivate
22640 * Fires before a tab is de-activated - can be used to do validation on a form.
22641 * @param {Roo.bootstrap.TabPanel} this
22642 * @return {Boolean} false if there is an error
22645 'beforedeactivate': true
22648 this.tabId = this.tabId || Roo.id();
22652 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22659 touchSlide : false,
22660 getAutoCreate : function(){
22665 // item is needed for carousel - not sure if it has any effect otherwise
22666 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22667 html: this.html || ''
22671 cfg.cls += ' active';
22675 cfg.tabId = this.tabId;
22683 initEvents: function()
22685 var p = this.parent();
22687 this.navId = this.navId || p.navId;
22689 if (typeof(this.navId) != 'undefined') {
22690 // not really needed.. but just in case.. parent should be a NavGroup.
22691 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22695 var i = tg.tabs.length - 1;
22697 if(this.active && tg.bullets > 0 && i < tg.bullets){
22698 tg.setActiveBullet(i);
22702 this.el.on('click', this.onClick, this);
22704 if(Roo.isTouch && this.touchSlide){
22705 this.el.on("touchstart", this.onTouchStart, this);
22706 this.el.on("touchmove", this.onTouchMove, this);
22707 this.el.on("touchend", this.onTouchEnd, this);
22712 onRender : function(ct, position)
22714 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22717 setActive : function(state)
22719 Roo.log("panel - set active " + this.tabId + "=" + state);
22721 this.active = state;
22723 this.el.removeClass('active');
22725 } else if (!this.el.hasClass('active')) {
22726 this.el.addClass('active');
22729 this.fireEvent('changed', this, state);
22732 onClick : function(e)
22734 e.preventDefault();
22736 if(!this.href.length){
22740 window.location.href = this.href;
22749 onTouchStart : function(e)
22751 this.swiping = false;
22753 this.startX = e.browserEvent.touches[0].clientX;
22754 this.startY = e.browserEvent.touches[0].clientY;
22757 onTouchMove : function(e)
22759 this.swiping = true;
22761 this.endX = e.browserEvent.touches[0].clientX;
22762 this.endY = e.browserEvent.touches[0].clientY;
22765 onTouchEnd : function(e)
22772 var tabGroup = this.parent();
22774 if(this.endX > this.startX){ // swiping right
22775 tabGroup.showPanelPrev();
22779 if(this.startX > this.endX){ // swiping left
22780 tabGroup.showPanelNext();
22799 * @class Roo.bootstrap.form.DateField
22800 * @extends Roo.bootstrap.form.Input
22801 * Bootstrap DateField class
22802 * @cfg {Number} weekStart default 0
22803 * @cfg {String} viewMode default empty, (months|years)
22804 * @cfg {String} minViewMode default empty, (months|years)
22805 * @cfg {Number} startDate default -Infinity
22806 * @cfg {Number} endDate default Infinity
22807 * @cfg {Boolean} todayHighlight default false
22808 * @cfg {Boolean} todayBtn default false
22809 * @cfg {Boolean} calendarWeeks default false
22810 * @cfg {Object} daysOfWeekDisabled default empty
22811 * @cfg {Boolean} singleMode default false (true | false)
22813 * @cfg {Boolean} keyboardNavigation default true
22814 * @cfg {String} language default en
22817 * Create a new DateField
22818 * @param {Object} config The config object
22821 Roo.bootstrap.form.DateField = function(config){
22822 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22826 * Fires when this field show.
22827 * @param {Roo.bootstrap.form.DateField} this
22828 * @param {Mixed} date The date value
22833 * Fires when this field hide.
22834 * @param {Roo.bootstrap.form.DateField} this
22835 * @param {Mixed} date The date value
22840 * Fires when select a date.
22841 * @param {Roo.bootstrap.form.DateField} this
22842 * @param {Mixed} date The date value
22846 * @event beforeselect
22847 * Fires when before select a date.
22848 * @param {Roo.bootstrap.form.DateField} this
22849 * @param {Mixed} date The date value
22851 beforeselect : true
22855 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22858 * @cfg {String} format
22859 * The default date format string which can be overriden for localization support. The format must be
22860 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22864 * @cfg {String} altFormats
22865 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22866 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22868 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22876 todayHighlight : false,
22882 keyboardNavigation: true,
22884 calendarWeeks: false,
22886 startDate: -Infinity,
22890 daysOfWeekDisabled: [],
22894 singleMode : false,
22896 UTCDate: function()
22898 return new Date(Date.UTC.apply(Date, arguments));
22901 UTCToday: function()
22903 var today = new Date();
22904 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22907 getDate: function() {
22908 var d = this.getUTCDate();
22909 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22912 getUTCDate: function() {
22916 setDate: function(d) {
22917 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22920 setUTCDate: function(d) {
22922 this.setValue(this.formatDate(this.date));
22925 onRender: function(ct, position)
22928 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22930 this.language = this.language || 'en';
22931 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22932 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22934 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22935 this.format = this.format || 'm/d/y';
22936 this.isInline = false;
22937 this.isInput = true;
22938 this.component = this.el.select('.add-on', true).first() || false;
22939 this.component = (this.component && this.component.length === 0) ? false : this.component;
22940 this.hasInput = this.component && this.inputEl().length;
22942 if (typeof(this.minViewMode === 'string')) {
22943 switch (this.minViewMode) {
22945 this.minViewMode = 1;
22948 this.minViewMode = 2;
22951 this.minViewMode = 0;
22956 if (typeof(this.viewMode === 'string')) {
22957 switch (this.viewMode) {
22970 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22972 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22974 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22976 this.picker().on('mousedown', this.onMousedown, this);
22977 this.picker().on('click', this.onClick, this);
22979 this.picker().addClass('datepicker-dropdown');
22981 this.startViewMode = this.viewMode;
22983 if(this.singleMode){
22984 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22985 v.setVisibilityMode(Roo.Element.DISPLAY);
22989 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22990 v.setStyle('width', '189px');
22994 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22995 if(!this.calendarWeeks){
23000 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23001 v.attr('colspan', function(i, val){
23002 return parseInt(val) + 1;
23007 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23009 this.setStartDate(this.startDate);
23010 this.setEndDate(this.endDate);
23012 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23019 if(this.isInline) {
23024 picker : function()
23026 return this.pickerEl;
23027 // return this.el.select('.datepicker', true).first();
23030 fillDow: function()
23032 var dowCnt = this.weekStart;
23041 if(this.calendarWeeks){
23049 while (dowCnt < this.weekStart + 7) {
23053 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23057 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23060 fillMonths: function()
23063 var months = this.picker().select('>.datepicker-months td', true).first();
23065 months.dom.innerHTML = '';
23071 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23074 months.createChild(month);
23081 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;
23083 if (this.date < this.startDate) {
23084 this.viewDate = new Date(this.startDate);
23085 } else if (this.date > this.endDate) {
23086 this.viewDate = new Date(this.endDate);
23088 this.viewDate = new Date(this.date);
23096 var d = new Date(this.viewDate),
23097 year = d.getUTCFullYear(),
23098 month = d.getUTCMonth(),
23099 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23100 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23101 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23102 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23103 currentDate = this.date && this.date.valueOf(),
23104 today = this.UTCToday();
23106 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23108 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23110 // this.picker.select('>tfoot th.today').
23111 // .text(dates[this.language].today)
23112 // .toggle(this.todayBtn !== false);
23114 this.updateNavArrows();
23117 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23119 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23121 prevMonth.setUTCDate(day);
23123 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23125 var nextMonth = new Date(prevMonth);
23127 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23129 nextMonth = nextMonth.valueOf();
23131 var fillMonths = false;
23133 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23135 while(prevMonth.valueOf() <= nextMonth) {
23138 if (prevMonth.getUTCDay() === this.weekStart) {
23140 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23148 if(this.calendarWeeks){
23149 // ISO 8601: First week contains first thursday.
23150 // ISO also states week starts on Monday, but we can be more abstract here.
23152 // Start of current week: based on weekstart/current date
23153 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23154 // Thursday of this week
23155 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23156 // First Thursday of year, year from thursday
23157 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23158 // Calendar week: ms between thursdays, div ms per day, div 7 days
23159 calWeek = (th - yth) / 864e5 / 7 + 1;
23161 fillMonths.cn.push({
23169 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23171 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23174 if (this.todayHighlight &&
23175 prevMonth.getUTCFullYear() == today.getFullYear() &&
23176 prevMonth.getUTCMonth() == today.getMonth() &&
23177 prevMonth.getUTCDate() == today.getDate()) {
23178 clsName += ' today';
23181 if (currentDate && prevMonth.valueOf() === currentDate) {
23182 clsName += ' active';
23185 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23186 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23187 clsName += ' disabled';
23190 fillMonths.cn.push({
23192 cls: 'day ' + clsName,
23193 html: prevMonth.getDate()
23196 prevMonth.setDate(prevMonth.getDate()+1);
23199 var currentYear = this.date && this.date.getUTCFullYear();
23200 var currentMonth = this.date && this.date.getUTCMonth();
23202 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23204 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23205 v.removeClass('active');
23207 if(currentYear === year && k === currentMonth){
23208 v.addClass('active');
23211 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23212 v.addClass('disabled');
23218 year = parseInt(year/10, 10) * 10;
23220 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23222 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23225 for (var i = -1; i < 11; i++) {
23226 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23228 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23236 showMode: function(dir)
23239 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23242 Roo.each(this.picker().select('>div',true).elements, function(v){
23243 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23246 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23251 if(this.isInline) {
23255 this.picker().removeClass(['bottom', 'top']);
23257 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23259 * place to the top of element!
23263 this.picker().addClass('top');
23264 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23269 this.picker().addClass('bottom');
23271 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23274 parseDate : function(value)
23276 if(!value || value instanceof Date){
23279 var v = Date.parseDate(value, this.format);
23280 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23281 v = Date.parseDate(value, 'Y-m-d');
23283 if(!v && this.altFormats){
23284 if(!this.altFormatsArray){
23285 this.altFormatsArray = this.altFormats.split("|");
23287 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23288 v = Date.parseDate(value, this.altFormatsArray[i]);
23294 formatDate : function(date, fmt)
23296 return (!date || !(date instanceof Date)) ?
23297 date : date.dateFormat(fmt || this.format);
23300 onFocus : function()
23302 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23306 onBlur : function()
23308 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23310 var d = this.inputEl().getValue();
23317 showPopup : function()
23319 this.picker().show();
23323 this.fireEvent('showpopup', this, this.date);
23326 hidePopup : function()
23328 if(this.isInline) {
23331 this.picker().hide();
23332 this.viewMode = this.startViewMode;
23335 this.fireEvent('hidepopup', this, this.date);
23339 onMousedown: function(e)
23341 e.stopPropagation();
23342 e.preventDefault();
23347 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23351 setValue: function(v)
23353 if(this.fireEvent('beforeselect', this, v) !== false){
23354 var d = new Date(this.parseDate(v) ).clearTime();
23356 if(isNaN(d.getTime())){
23357 this.date = this.viewDate = '';
23358 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23362 v = this.formatDate(d);
23364 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23366 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23370 this.fireEvent('select', this, this.date);
23374 getValue: function()
23376 return this.formatDate(this.date);
23379 fireKey: function(e)
23381 if (!this.picker().isVisible()){
23382 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23388 var dateChanged = false,
23390 newDate, newViewDate;
23395 e.preventDefault();
23399 if (!this.keyboardNavigation) {
23402 dir = e.keyCode == 37 ? -1 : 1;
23405 newDate = this.moveYear(this.date, dir);
23406 newViewDate = this.moveYear(this.viewDate, dir);
23407 } else if (e.shiftKey){
23408 newDate = this.moveMonth(this.date, dir);
23409 newViewDate = this.moveMonth(this.viewDate, dir);
23411 newDate = new Date(this.date);
23412 newDate.setUTCDate(this.date.getUTCDate() + dir);
23413 newViewDate = new Date(this.viewDate);
23414 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23416 if (this.dateWithinRange(newDate)){
23417 this.date = newDate;
23418 this.viewDate = newViewDate;
23419 this.setValue(this.formatDate(this.date));
23421 e.preventDefault();
23422 dateChanged = true;
23427 if (!this.keyboardNavigation) {
23430 dir = e.keyCode == 38 ? -1 : 1;
23432 newDate = this.moveYear(this.date, dir);
23433 newViewDate = this.moveYear(this.viewDate, dir);
23434 } else if (e.shiftKey){
23435 newDate = this.moveMonth(this.date, dir);
23436 newViewDate = this.moveMonth(this.viewDate, dir);
23438 newDate = new Date(this.date);
23439 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23440 newViewDate = new Date(this.viewDate);
23441 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23443 if (this.dateWithinRange(newDate)){
23444 this.date = newDate;
23445 this.viewDate = newViewDate;
23446 this.setValue(this.formatDate(this.date));
23448 e.preventDefault();
23449 dateChanged = true;
23453 this.setValue(this.formatDate(this.date));
23455 e.preventDefault();
23458 this.setValue(this.formatDate(this.date));
23472 onClick: function(e)
23474 e.stopPropagation();
23475 e.preventDefault();
23477 var target = e.getTarget();
23479 if(target.nodeName.toLowerCase() === 'i'){
23480 target = Roo.get(target).dom.parentNode;
23483 var nodeName = target.nodeName;
23484 var className = target.className;
23485 var html = target.innerHTML;
23486 //Roo.log(nodeName);
23488 switch(nodeName.toLowerCase()) {
23490 switch(className) {
23496 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23497 switch(this.viewMode){
23499 this.viewDate = this.moveMonth(this.viewDate, dir);
23503 this.viewDate = this.moveYear(this.viewDate, dir);
23509 var date = new Date();
23510 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23512 this.setValue(this.formatDate(this.date));
23519 if (className.indexOf('disabled') < 0) {
23520 if (!this.viewDate) {
23521 this.viewDate = new Date();
23523 this.viewDate.setUTCDate(1);
23524 if (className.indexOf('month') > -1) {
23525 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23527 var year = parseInt(html, 10) || 0;
23528 this.viewDate.setUTCFullYear(year);
23532 if(this.singleMode){
23533 this.setValue(this.formatDate(this.viewDate));
23544 //Roo.log(className);
23545 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23546 var day = parseInt(html, 10) || 1;
23547 var year = (this.viewDate || new Date()).getUTCFullYear(),
23548 month = (this.viewDate || new Date()).getUTCMonth();
23550 if (className.indexOf('old') > -1) {
23557 } else if (className.indexOf('new') > -1) {
23565 //Roo.log([year,month,day]);
23566 this.date = this.UTCDate(year, month, day,0,0,0,0);
23567 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23569 //Roo.log(this.formatDate(this.date));
23570 this.setValue(this.formatDate(this.date));
23577 setStartDate: function(startDate)
23579 this.startDate = startDate || -Infinity;
23580 if (this.startDate !== -Infinity) {
23581 this.startDate = this.parseDate(this.startDate);
23584 this.updateNavArrows();
23587 setEndDate: function(endDate)
23589 this.endDate = endDate || Infinity;
23590 if (this.endDate !== Infinity) {
23591 this.endDate = this.parseDate(this.endDate);
23594 this.updateNavArrows();
23597 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23599 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23600 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23601 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23603 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23604 return parseInt(d, 10);
23607 this.updateNavArrows();
23610 updateNavArrows: function()
23612 if(this.singleMode){
23616 var d = new Date(this.viewDate),
23617 year = d.getUTCFullYear(),
23618 month = d.getUTCMonth();
23620 Roo.each(this.picker().select('.prev', true).elements, function(v){
23622 switch (this.viewMode) {
23625 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23631 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23638 Roo.each(this.picker().select('.next', true).elements, function(v){
23640 switch (this.viewMode) {
23643 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23649 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23657 moveMonth: function(date, dir)
23662 var new_date = new Date(date.valueOf()),
23663 day = new_date.getUTCDate(),
23664 month = new_date.getUTCMonth(),
23665 mag = Math.abs(dir),
23667 dir = dir > 0 ? 1 : -1;
23670 // If going back one month, make sure month is not current month
23671 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23673 return new_date.getUTCMonth() == month;
23675 // If going forward one month, make sure month is as expected
23676 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23678 return new_date.getUTCMonth() != new_month;
23680 new_month = month + dir;
23681 new_date.setUTCMonth(new_month);
23682 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23683 if (new_month < 0 || new_month > 11) {
23684 new_month = (new_month + 12) % 12;
23687 // For magnitudes >1, move one month at a time...
23688 for (var i=0; i<mag; i++) {
23689 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23690 new_date = this.moveMonth(new_date, dir);
23692 // ...then reset the day, keeping it in the new month
23693 new_month = new_date.getUTCMonth();
23694 new_date.setUTCDate(day);
23696 return new_month != new_date.getUTCMonth();
23699 // Common date-resetting loop -- if date is beyond end of month, make it
23702 new_date.setUTCDate(--day);
23703 new_date.setUTCMonth(new_month);
23708 moveYear: function(date, dir)
23710 return this.moveMonth(date, dir*12);
23713 dateWithinRange: function(date)
23715 return date >= this.startDate && date <= this.endDate;
23721 this.picker().remove();
23724 validateValue : function(value)
23726 if(this.getVisibilityEl().hasClass('hidden')){
23730 if(value.length < 1) {
23731 if(this.allowBlank){
23737 if(value.length < this.minLength){
23740 if(value.length > this.maxLength){
23744 var vt = Roo.form.VTypes;
23745 if(!vt[this.vtype](value, this)){
23749 if(typeof this.validator == "function"){
23750 var msg = this.validator(value);
23756 if(this.regex && !this.regex.test(value)){
23760 if(typeof(this.parseDate(value)) == 'undefined'){
23764 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23768 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23778 this.date = this.viewDate = '';
23780 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23785 Roo.apply(Roo.bootstrap.form.DateField, {
23796 html: '<i class="fa fa-arrow-left"/>'
23806 html: '<i class="fa fa-arrow-right"/>'
23848 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23849 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23850 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23851 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23852 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23865 navFnc: 'FullYear',
23870 navFnc: 'FullYear',
23875 Roo.apply(Roo.bootstrap.form.DateField, {
23879 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23883 cls: 'datepicker-days',
23887 cls: 'table-condensed',
23889 Roo.bootstrap.form.DateField.head,
23893 Roo.bootstrap.form.DateField.footer
23900 cls: 'datepicker-months',
23904 cls: 'table-condensed',
23906 Roo.bootstrap.form.DateField.head,
23907 Roo.bootstrap.form.DateField.content,
23908 Roo.bootstrap.form.DateField.footer
23915 cls: 'datepicker-years',
23919 cls: 'table-condensed',
23921 Roo.bootstrap.form.DateField.head,
23922 Roo.bootstrap.form.DateField.content,
23923 Roo.bootstrap.form.DateField.footer
23942 * @class Roo.bootstrap.form.TimeField
23943 * @extends Roo.bootstrap.form.Input
23944 * Bootstrap DateField class
23945 * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23949 * Create a new TimeField
23950 * @param {Object} config The config object
23953 Roo.bootstrap.form.TimeField = function(config){
23954 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23958 * Fires when this field show.
23959 * @param {Roo.bootstrap.form.DateField} thisthis
23960 * @param {Mixed} date The date value
23965 * Fires when this field hide.
23966 * @param {Roo.bootstrap.form.DateField} this
23967 * @param {Mixed} date The date value
23972 * Fires when select a date.
23973 * @param {Roo.bootstrap.form.DateField} this
23974 * @param {Mixed} date The date value
23980 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23983 * @cfg {String} format
23984 * The default time format string which can be overriden for localization support. The format must be
23985 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23990 getAutoCreate : function()
23992 this.after = '<i class="fa far fa-clock"></i>';
23993 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23997 onRender: function(ct, position)
24000 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24002 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24004 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24006 this.pop = this.picker().select('>.datepicker-time',true).first();
24007 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24009 this.picker().on('mousedown', this.onMousedown, this);
24010 this.picker().on('click', this.onClick, this);
24012 this.picker().addClass('datepicker-dropdown');
24017 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24018 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24019 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24020 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24021 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24022 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24026 fireKey: function(e){
24027 if (!this.picker().isVisible()){
24028 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24034 e.preventDefault();
24042 this.onTogglePeriod();
24045 this.onIncrementMinutes();
24048 this.onDecrementMinutes();
24057 onClick: function(e) {
24058 e.stopPropagation();
24059 e.preventDefault();
24062 picker : function()
24064 return this.pickerEl;
24067 fillTime: function()
24069 var time = this.pop.select('tbody', true).first();
24071 time.dom.innerHTML = '';
24086 cls: 'hours-up fa fas fa-chevron-up'
24106 cls: 'minutes-up fa fas fa-chevron-up'
24127 cls: 'timepicker-hour',
24142 cls: 'timepicker-minute',
24157 cls: 'btn btn-primary period',
24179 cls: 'hours-down fa fas fa-chevron-down'
24199 cls: 'minutes-down fa fas fa-chevron-down'
24216 // default minute is a multiple of minuteStep
24217 if(typeof(this.time) === 'undefined') {
24218 this.time = new Date();
24219 this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24221 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24228 var hours = this.time.getHours();
24229 var minutes = this.time.getMinutes();
24242 hours = hours - 12;
24246 hours = '0' + hours;
24250 minutes = '0' + minutes;
24253 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24254 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24255 this.pop.select('button', true).first().dom.innerHTML = period;
24261 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24263 var cls = ['bottom'];
24265 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24272 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24276 //this.picker().setXY(20000,20000);
24277 this.picker().addClass(cls.join('-'));
24281 Roo.each(cls, function(c){
24286 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24287 //_this.picker().setTop(_this.inputEl().getHeight());
24291 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24293 //_this.picker().setTop(0 - _this.picker().getHeight());
24298 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24302 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24310 onFocus : function()
24312 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24316 onBlur : function()
24318 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24324 this.picker().show();
24329 this.fireEvent('show', this, this.date);
24334 this.picker().hide();
24337 this.fireEvent('hide', this, this.date);
24340 setTime : function()
24343 this.setValue(this.time.format(this.format));
24345 this.fireEvent('select', this, this.date);
24350 onMousedown: function(e){
24351 e.stopPropagation();
24352 e.preventDefault();
24355 onIncrementHours: function()
24357 Roo.log('onIncrementHours');
24358 this.time = this.time.add(Date.HOUR, 1);
24363 onDecrementHours: function()
24365 Roo.log('onDecrementHours');
24366 this.time = this.time.add(Date.HOUR, -1);
24370 onIncrementMinutes: function()
24372 Roo.log('onIncrementMinutes');
24373 var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24374 this.time = this.time.add(Date.MINUTE, minutesToAdd);
24378 onDecrementMinutes: function()
24380 Roo.log('onDecrementMinutes');
24381 var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24382 this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24386 onTogglePeriod: function()
24388 Roo.log('onTogglePeriod');
24389 this.time = this.time.add(Date.HOUR, 12);
24397 Roo.apply(Roo.bootstrap.form.TimeField, {
24401 cls: 'datepicker dropdown-menu',
24405 cls: 'datepicker-time',
24409 cls: 'table-condensed',
24438 cls: 'btn btn-info ok',
24466 * @class Roo.bootstrap.form.MonthField
24467 * @extends Roo.bootstrap.form.Input
24468 * Bootstrap MonthField class
24470 * @cfg {String} language default en
24473 * Create a new MonthField
24474 * @param {Object} config The config object
24477 Roo.bootstrap.form.MonthField = function(config){
24478 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24483 * Fires when this field show.
24484 * @param {Roo.bootstrap.form.MonthField} this
24485 * @param {Mixed} date The date value
24490 * Fires when this field hide.
24491 * @param {Roo.bootstrap.form.MonthField} this
24492 * @param {Mixed} date The date value
24497 * Fires when select a date.
24498 * @param {Roo.bootstrap.form.MonthField} this
24499 * @param {String} oldvalue The old value
24500 * @param {String} newvalue The new value
24506 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24508 onRender: function(ct, position)
24511 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24513 this.language = this.language || 'en';
24514 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24515 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24517 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24518 this.isInline = false;
24519 this.isInput = true;
24520 this.component = this.el.select('.add-on', true).first() || false;
24521 this.component = (this.component && this.component.length === 0) ? false : this.component;
24522 this.hasInput = this.component && this.inputEL().length;
24524 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24526 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24528 this.picker().on('mousedown', this.onMousedown, this);
24529 this.picker().on('click', this.onClick, this);
24531 this.picker().addClass('datepicker-dropdown');
24533 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24534 v.setStyle('width', '189px');
24541 if(this.isInline) {
24547 setValue: function(v, suppressEvent)
24549 var o = this.getValue();
24551 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24555 if(suppressEvent !== true){
24556 this.fireEvent('select', this, o, v);
24561 getValue: function()
24566 onClick: function(e)
24568 e.stopPropagation();
24569 e.preventDefault();
24571 var target = e.getTarget();
24573 if(target.nodeName.toLowerCase() === 'i'){
24574 target = Roo.get(target).dom.parentNode;
24577 var nodeName = target.nodeName;
24578 var className = target.className;
24579 var html = target.innerHTML;
24581 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24585 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24587 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24593 picker : function()
24595 return this.pickerEl;
24598 fillMonths: function()
24601 var months = this.picker().select('>.datepicker-months td', true).first();
24603 months.dom.innerHTML = '';
24609 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24612 months.createChild(month);
24621 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24622 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24625 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24626 e.removeClass('active');
24628 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24629 e.addClass('active');
24636 if(this.isInline) {
24640 this.picker().removeClass(['bottom', 'top']);
24642 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24644 * place to the top of element!
24648 this.picker().addClass('top');
24649 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24654 this.picker().addClass('bottom');
24656 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24659 onFocus : function()
24661 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24665 onBlur : function()
24667 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24669 var d = this.inputEl().getValue();
24678 this.picker().show();
24679 this.picker().select('>.datepicker-months', true).first().show();
24683 this.fireEvent('show', this, this.date);
24688 if(this.isInline) {
24691 this.picker().hide();
24692 this.fireEvent('hide', this, this.date);
24696 onMousedown: function(e)
24698 e.stopPropagation();
24699 e.preventDefault();
24704 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24708 fireKey: function(e)
24710 if (!this.picker().isVisible()){
24711 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24722 e.preventDefault();
24726 dir = e.keyCode == 37 ? -1 : 1;
24728 this.vIndex = this.vIndex + dir;
24730 if(this.vIndex < 0){
24734 if(this.vIndex > 11){
24738 if(isNaN(this.vIndex)){
24742 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24748 dir = e.keyCode == 38 ? -1 : 1;
24750 this.vIndex = this.vIndex + dir * 4;
24752 if(this.vIndex < 0){
24756 if(this.vIndex > 11){
24760 if(isNaN(this.vIndex)){
24764 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24769 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24770 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24774 e.preventDefault();
24777 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24778 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24794 this.picker().remove();
24799 Roo.apply(Roo.bootstrap.form.MonthField, {
24818 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24819 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24824 Roo.apply(Roo.bootstrap.form.MonthField, {
24828 cls: 'datepicker dropdown-menu roo-dynamic',
24832 cls: 'datepicker-months',
24836 cls: 'table-condensed',
24838 Roo.bootstrap.form.DateField.content
24858 * @class Roo.bootstrap.form.CheckBox
24859 * @extends Roo.bootstrap.form.Input
24860 * Bootstrap CheckBox class
24862 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24863 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24864 * @cfg {String} boxLabel The text that appears beside the checkbox
24865 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24866 * @cfg {Boolean} checked initnal the element
24867 * @cfg {Boolean} inline inline the element (default false)
24868 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24869 * @cfg {String} tooltip label tooltip
24872 * Create a new CheckBox
24873 * @param {Object} config The config object
24876 Roo.bootstrap.form.CheckBox = function(config){
24877 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24882 * Fires when the element is checked or unchecked.
24883 * @param {Roo.bootstrap.form.CheckBox} this This input
24884 * @param {Boolean} checked The new checked value
24889 * Fires when the element is click.
24890 * @param {Roo.bootstrap.form.CheckBox} this This input
24897 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24899 inputType: 'checkbox',
24908 // checkbox success does not make any sense really..
24913 getAutoCreate : function()
24915 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24921 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24924 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24930 type : this.inputType,
24931 value : this.inputValue,
24932 cls : 'roo-' + this.inputType, //'form-box',
24933 placeholder : this.placeholder || ''
24937 if(this.inputType != 'radio'){
24941 cls : 'roo-hidden-value',
24942 value : this.checked ? this.inputValue : this.valueOff
24947 if (this.weight) { // Validity check?
24948 cfg.cls += " " + this.inputType + "-" + this.weight;
24951 if (this.disabled) {
24952 input.disabled=true;
24956 input.checked = this.checked;
24961 input.name = this.name;
24963 if(this.inputType != 'radio'){
24964 hidden.name = this.name;
24965 input.name = '_hidden_' + this.name;
24970 input.cls += ' input-' + this.size;
24975 ['xs','sm','md','lg'].map(function(size){
24976 if (settings[size]) {
24977 cfg.cls += ' col-' + size + '-' + settings[size];
24981 var inputblock = input;
24983 if (this.before || this.after) {
24986 cls : 'input-group',
24991 inputblock.cn.push({
24993 cls : 'input-group-addon',
24998 inputblock.cn.push(input);
25000 if(this.inputType != 'radio'){
25001 inputblock.cn.push(hidden);
25005 inputblock.cn.push({
25007 cls : 'input-group-addon',
25013 var boxLabelCfg = false;
25019 //'for': id, // box label is handled by onclick - so no for...
25021 html: this.boxLabel
25024 boxLabelCfg.tooltip = this.tooltip;
25030 if (align ==='left' && this.fieldLabel.length) {
25031 // Roo.log("left and has label");
25036 cls : 'control-label',
25037 html : this.fieldLabel
25048 cfg.cn[1].cn.push(boxLabelCfg);
25051 if(this.labelWidth > 12){
25052 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25055 if(this.labelWidth < 13 && this.labelmd == 0){
25056 this.labelmd = this.labelWidth;
25059 if(this.labellg > 0){
25060 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25061 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25064 if(this.labelmd > 0){
25065 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25066 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25069 if(this.labelsm > 0){
25070 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25071 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25074 if(this.labelxs > 0){
25075 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25076 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25079 } else if ( this.fieldLabel.length) {
25080 // Roo.log(" label");
25084 tag: this.boxLabel ? 'span' : 'label',
25086 cls: 'control-label box-input-label',
25087 //cls : 'input-group-addon',
25088 html : this.fieldLabel
25095 cfg.cn.push(boxLabelCfg);
25100 // Roo.log(" no label && no align");
25101 cfg.cn = [ inputblock ] ;
25103 cfg.cn.push(boxLabelCfg);
25111 if(this.inputType != 'radio'){
25112 cfg.cn.push(hidden);
25120 * return the real input element.
25122 inputEl: function ()
25124 return this.el.select('input.roo-' + this.inputType,true).first();
25126 hiddenEl: function ()
25128 return this.el.select('input.roo-hidden-value',true).first();
25131 labelEl: function()
25133 return this.el.select('label.control-label',true).first();
25135 /* depricated... */
25139 return this.labelEl();
25142 boxLabelEl: function()
25144 return this.el.select('label.box-label',true).first();
25147 initEvents : function()
25149 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25151 this.inputEl().on('click', this.onClick, this);
25153 if (this.boxLabel) {
25154 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25157 this.startValue = this.getValue();
25160 Roo.bootstrap.form.CheckBox.register(this);
25164 onClick : function(e)
25166 if(this.fireEvent('click', this, e) !== false){
25167 this.setChecked(!this.checked);
25172 setChecked : function(state,suppressEvent)
25174 this.startValue = this.getValue();
25176 if(this.inputType == 'radio'){
25178 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25179 e.dom.checked = false;
25182 this.inputEl().dom.checked = true;
25184 this.inputEl().dom.value = this.inputValue;
25186 if(suppressEvent !== true){
25187 this.fireEvent('check', this, true);
25195 this.checked = state;
25197 this.inputEl().dom.checked = state;
25200 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25202 if(suppressEvent !== true){
25203 this.fireEvent('check', this, state);
25209 getValue : function()
25211 if(this.inputType == 'radio'){
25212 return this.getGroupValue();
25215 return this.hiddenEl().dom.value;
25219 getGroupValue : function()
25221 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25225 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25228 setValue : function(v,suppressEvent)
25230 if(this.inputType == 'radio'){
25231 this.setGroupValue(v, suppressEvent);
25235 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25240 setGroupValue : function(v, suppressEvent)
25242 this.startValue = this.getValue();
25244 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25245 e.dom.checked = false;
25247 if(e.dom.value == v){
25248 e.dom.checked = true;
25252 if(suppressEvent !== true){
25253 this.fireEvent('check', this, true);
25261 validate : function()
25263 if(this.getVisibilityEl().hasClass('hidden')){
25269 (this.inputType == 'radio' && this.validateRadio()) ||
25270 (this.inputType == 'checkbox' && this.validateCheckbox())
25276 this.markInvalid();
25280 validateRadio : function()
25282 if(this.getVisibilityEl().hasClass('hidden')){
25286 if(this.allowBlank){
25292 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25293 if(!e.dom.checked){
25305 validateCheckbox : function()
25308 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25309 //return (this.getValue() == this.inputValue) ? true : false;
25312 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25320 for(var i in group){
25321 if(group[i].el.isVisible(true)){
25329 for(var i in group){
25334 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25341 * Mark this field as valid
25343 markValid : function()
25347 this.fireEvent('valid', this);
25349 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25352 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25359 if(this.inputType == 'radio'){
25360 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25361 var fg = e.findParent('.form-group', false, true);
25362 if (Roo.bootstrap.version == 3) {
25363 fg.removeClass([_this.invalidClass, _this.validClass]);
25364 fg.addClass(_this.validClass);
25366 fg.removeClass(['is-valid', 'is-invalid']);
25367 fg.addClass('is-valid');
25375 var fg = this.el.findParent('.form-group', false, true);
25376 if (Roo.bootstrap.version == 3) {
25377 fg.removeClass([this.invalidClass, this.validClass]);
25378 fg.addClass(this.validClass);
25380 fg.removeClass(['is-valid', 'is-invalid']);
25381 fg.addClass('is-valid');
25386 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25392 for(var i in group){
25393 var fg = group[i].el.findParent('.form-group', false, true);
25394 if (Roo.bootstrap.version == 3) {
25395 fg.removeClass([this.invalidClass, this.validClass]);
25396 fg.addClass(this.validClass);
25398 fg.removeClass(['is-valid', 'is-invalid']);
25399 fg.addClass('is-valid');
25405 * Mark this field as invalid
25406 * @param {String} msg The validation message
25408 markInvalid : function(msg)
25410 if(this.allowBlank){
25416 this.fireEvent('invalid', this, msg);
25418 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25421 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25425 label.markInvalid();
25428 if(this.inputType == 'radio'){
25430 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25431 var fg = e.findParent('.form-group', false, true);
25432 if (Roo.bootstrap.version == 3) {
25433 fg.removeClass([_this.invalidClass, _this.validClass]);
25434 fg.addClass(_this.invalidClass);
25436 fg.removeClass(['is-invalid', 'is-valid']);
25437 fg.addClass('is-invalid');
25445 var fg = this.el.findParent('.form-group', false, true);
25446 if (Roo.bootstrap.version == 3) {
25447 fg.removeClass([_this.invalidClass, _this.validClass]);
25448 fg.addClass(_this.invalidClass);
25450 fg.removeClass(['is-invalid', 'is-valid']);
25451 fg.addClass('is-invalid');
25456 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25462 for(var i in group){
25463 var fg = group[i].el.findParent('.form-group', false, true);
25464 if (Roo.bootstrap.version == 3) {
25465 fg.removeClass([_this.invalidClass, _this.validClass]);
25466 fg.addClass(_this.invalidClass);
25468 fg.removeClass(['is-invalid', 'is-valid']);
25469 fg.addClass('is-invalid');
25475 clearInvalid : function()
25477 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25479 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25481 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25483 if (label && label.iconEl) {
25484 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25485 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25489 disable : function()
25491 if(this.inputType != 'radio'){
25492 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25499 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25500 _this.getActionEl().addClass(this.disabledClass);
25501 e.dom.disabled = true;
25505 this.disabled = true;
25506 this.fireEvent("disable", this);
25510 enable : function()
25512 if(this.inputType != 'radio'){
25513 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25520 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25521 _this.getActionEl().removeClass(this.disabledClass);
25522 e.dom.disabled = false;
25526 this.disabled = false;
25527 this.fireEvent("enable", this);
25531 setBoxLabel : function(v)
25536 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25542 Roo.apply(Roo.bootstrap.form.CheckBox, {
25547 * register a CheckBox Group
25548 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25550 register : function(checkbox)
25552 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25553 this.groups[checkbox.groupId] = {};
25556 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25560 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25564 * fetch a CheckBox Group based on the group ID
25565 * @param {string} the group ID
25566 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25568 get: function(groupId) {
25569 if (typeof(this.groups[groupId]) == 'undefined') {
25573 return this.groups[groupId] ;
25586 * @class Roo.bootstrap.form.Radio
25587 * @extends Roo.bootstrap.Component
25588 * Bootstrap Radio class
25589 * @cfg {String} boxLabel - the label associated
25590 * @cfg {String} value - the value of radio
25593 * Create a new Radio
25594 * @param {Object} config The config object
25596 Roo.bootstrap.form.Radio = function(config){
25597 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25601 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25607 getAutoCreate : function()
25611 cls : 'form-group radio',
25616 html : this.boxLabel
25624 initEvents : function()
25626 this.parent().register(this);
25628 this.el.on('click', this.onClick, this);
25632 onClick : function(e)
25634 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25635 this.setChecked(true);
25639 setChecked : function(state, suppressEvent)
25641 this.parent().setValue(this.value, suppressEvent);
25645 setBoxLabel : function(v)
25650 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25665 * @class Roo.bootstrap.form.SecurePass
25666 * @extends Roo.bootstrap.form.Input
25667 * Bootstrap SecurePass class
25671 * Create a new SecurePass
25672 * @param {Object} config The config object
25675 Roo.bootstrap.form.SecurePass = function (config) {
25676 // these go here, so the translation tool can replace them..
25678 PwdEmpty: "Please type a password, and then retype it to confirm.",
25679 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25680 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25681 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25682 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25683 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25684 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25685 TooWeak: "Your password is Too Weak."
25687 this.meterLabel = "Password strength:";
25688 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25689 this.meterClass = [
25690 "roo-password-meter-tooweak",
25691 "roo-password-meter-weak",
25692 "roo-password-meter-medium",
25693 "roo-password-meter-strong",
25694 "roo-password-meter-grey"
25699 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25702 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25704 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25706 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25707 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25708 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25709 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25710 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25711 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25712 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25722 * @cfg {String/Object} Label for the strength meter (defaults to
25723 * 'Password strength:')
25728 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25729 * ['Weak', 'Medium', 'Strong'])
25732 pwdStrengths: false,
25745 initEvents: function ()
25747 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25749 if (this.el.is('input[type=password]') && Roo.isSafari) {
25750 this.el.on('keydown', this.SafariOnKeyDown, this);
25753 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25756 onRender: function (ct, position)
25758 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25759 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25760 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25762 this.trigger.createChild({
25767 cls: 'roo-password-meter-grey col-xs-12',
25770 //width: this.meterWidth + 'px'
25774 cls: 'roo-password-meter-text'
25780 if (this.hideTrigger) {
25781 this.trigger.setDisplayed(false);
25783 this.setSize(this.width || '', this.height || '');
25786 onDestroy: function ()
25788 if (this.trigger) {
25789 this.trigger.removeAllListeners();
25790 this.trigger.remove();
25793 this.wrap.remove();
25795 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25798 checkStrength: function ()
25800 var pwd = this.inputEl().getValue();
25801 if (pwd == this._lastPwd) {
25806 if (this.ClientSideStrongPassword(pwd)) {
25808 } else if (this.ClientSideMediumPassword(pwd)) {
25810 } else if (this.ClientSideWeakPassword(pwd)) {
25816 Roo.log('strength1: ' + strength);
25818 //var pm = this.trigger.child('div/div/div').dom;
25819 var pm = this.trigger.child('div/div');
25820 pm.removeClass(this.meterClass);
25821 pm.addClass(this.meterClass[strength]);
25824 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25826 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25828 this._lastPwd = pwd;
25832 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25834 this._lastPwd = '';
25836 var pm = this.trigger.child('div/div');
25837 pm.removeClass(this.meterClass);
25838 pm.addClass('roo-password-meter-grey');
25841 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25844 this.inputEl().dom.type='password';
25847 validateValue: function (value)
25849 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25852 if (value.length == 0) {
25853 if (this.allowBlank) {
25854 this.clearInvalid();
25858 this.markInvalid(this.errors.PwdEmpty);
25859 this.errorMsg = this.errors.PwdEmpty;
25867 if (!value.match(/[\x21-\x7e]+/)) {
25868 this.markInvalid(this.errors.PwdBadChar);
25869 this.errorMsg = this.errors.PwdBadChar;
25872 if (value.length < 6) {
25873 this.markInvalid(this.errors.PwdShort);
25874 this.errorMsg = this.errors.PwdShort;
25877 if (value.length > 16) {
25878 this.markInvalid(this.errors.PwdLong);
25879 this.errorMsg = this.errors.PwdLong;
25883 if (this.ClientSideStrongPassword(value)) {
25885 } else if (this.ClientSideMediumPassword(value)) {
25887 } else if (this.ClientSideWeakPassword(value)) {
25894 if (strength < 2) {
25895 //this.markInvalid(this.errors.TooWeak);
25896 this.errorMsg = this.errors.TooWeak;
25901 console.log('strength2: ' + strength);
25903 //var pm = this.trigger.child('div/div/div').dom;
25905 var pm = this.trigger.child('div/div');
25906 pm.removeClass(this.meterClass);
25907 pm.addClass(this.meterClass[strength]);
25909 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25911 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25913 this.errorMsg = '';
25917 CharacterSetChecks: function (type)
25920 this.fResult = false;
25923 isctype: function (character, type)
25926 case this.kCapitalLetter:
25927 if (character >= 'A' && character <= 'Z') {
25932 case this.kSmallLetter:
25933 if (character >= 'a' && character <= 'z') {
25939 if (character >= '0' && character <= '9') {
25944 case this.kPunctuation:
25945 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25956 IsLongEnough: function (pwd, size)
25958 return !(pwd == null || isNaN(size) || pwd.length < size);
25961 SpansEnoughCharacterSets: function (word, nb)
25963 if (!this.IsLongEnough(word, nb))
25968 var characterSetChecks = new Array(
25969 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25970 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25973 for (var index = 0; index < word.length; ++index) {
25974 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25975 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25976 characterSetChecks[nCharSet].fResult = true;
25983 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25984 if (characterSetChecks[nCharSet].fResult) {
25989 if (nCharSets < nb) {
25995 ClientSideStrongPassword: function (pwd)
25997 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26000 ClientSideMediumPassword: function (pwd)
26002 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26005 ClientSideWeakPassword: function (pwd)
26007 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26010 });Roo.rtf = {}; // namespace
26011 Roo.rtf.Hex = function(hex)
26015 Roo.rtf.Paragraph = function(opts)
26017 this.content = []; ///??? is that used?
26018 };Roo.rtf.Span = function(opts)
26020 this.value = opts.value;
26023 Roo.rtf.Group = function(parent)
26025 // we dont want to acutally store parent - it will make debug a nightmare..
26033 Roo.rtf.Group.prototype = {
26037 addContent : function(node) {
26038 // could set styles...
26039 this.content.push(node);
26041 addChild : function(cn)
26045 // only for images really...
26046 toDataURL : function()
26048 var mimetype = false;
26050 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
26051 mimetype = "image/png";
26053 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26054 mimetype = "image/jpeg";
26057 return 'about:blank'; // ?? error?
26061 var hexstring = this.content[this.content.length-1].value;
26063 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26064 return String.fromCharCode(parseInt(a, 16));
26069 // this looks like it's normally the {rtf{ .... }}
26070 Roo.rtf.Document = function()
26072 // we dont want to acutally store parent - it will make debug a nightmare..
26078 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
26079 addChild : function(cn)
26083 case 'rtlch': // most content seems to be inside this??
26086 this.rtlch.push(cn);
26089 this[cn.type] = cn;
26094 getElementsByType : function(type)
26097 this._getElementsByType(type, ret, this.cn, 'rtf');
26100 _getElementsByType : function (type, ret, search_array, path)
26102 search_array.forEach(function(n,i) {
26103 if (n.type == type) {
26104 n.path = path + '/' + n.type + ':' + i;
26107 if (n.cn.length > 0) {
26108 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26115 Roo.rtf.Ctrl = function(opts)
26117 this.value = opts.value;
26118 this.param = opts.param;
26123 * based on this https://github.com/iarna/rtf-parser
26124 * it's really only designed to extract pict from pasted RTF
26128 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26137 Roo.rtf.Parser = function(text) {
26138 //super({objectMode: true})
26140 this.parserState = this.parseText;
26142 // these are for interpeter...
26144 ///this.parserState = this.parseTop
26145 this.groupStack = [];
26146 this.hexStore = [];
26149 this.groups = []; // where we put the return.
26151 for (var ii = 0; ii < text.length; ++ii) {
26154 if (text[ii] === '\n') {
26160 this.parserState(text[ii]);
26166 Roo.rtf.Parser.prototype = {
26167 text : '', // string being parsed..
26169 controlWordParam : '',
26173 groupStack : false,
26178 row : 1, // reportin?
26182 push : function (el)
26184 var m = 'cmd'+ el.type;
26185 if (typeof(this[m]) == 'undefined') {
26186 Roo.log('invalid cmd:' + el.type);
26192 flushHexStore : function()
26194 if (this.hexStore.length < 1) {
26197 var hexstr = this.hexStore.map(
26202 this.group.addContent( new Roo.rtf.Hex( hexstr ));
26205 this.hexStore.splice(0)
26209 cmdgroupstart : function()
26211 this.flushHexStore();
26213 this.groupStack.push(this.group);
26216 if (this.doc === false) {
26217 this.group = this.doc = new Roo.rtf.Document();
26221 this.group = new Roo.rtf.Group(this.group);
26223 cmdignorable : function()
26225 this.flushHexStore();
26226 this.group.ignorable = true;
26228 cmdendparagraph : function()
26230 this.flushHexStore();
26231 this.group.addContent(new Roo.rtf.Paragraph());
26233 cmdgroupend : function ()
26235 this.flushHexStore();
26236 var endingGroup = this.group;
26239 this.group = this.groupStack.pop();
26241 this.group.addChild(endingGroup);
26246 var doc = this.group || this.doc;
26247 //if (endingGroup instanceof FontTable) {
26248 // doc.fonts = endingGroup.table
26249 //} else if (endingGroup instanceof ColorTable) {
26250 // doc.colors = endingGroup.table
26251 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26252 if (endingGroup.ignorable === false) {
26254 this.groups.push(endingGroup);
26255 // Roo.log( endingGroup );
26257 //Roo.each(endingGroup.content, function(item)) {
26258 // doc.addContent(item);
26260 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26263 cmdtext : function (cmd)
26265 this.flushHexStore();
26266 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26267 //this.group = this.doc
26268 return; // we really don't care about stray text...
26270 this.group.addContent(new Roo.rtf.Span(cmd));
26272 cmdcontrolword : function (cmd)
26274 this.flushHexStore();
26275 if (!this.group.type) {
26276 this.group.type = cmd.value;
26279 this.group.addContent(new Roo.rtf.Ctrl(cmd));
26280 // we actually don't care about ctrl words...
26283 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26284 if (this[method]) {
26285 this[method](cmd.param)
26287 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26291 cmdhexchar : function(cmd) {
26292 this.hexStore.push(cmd);
26294 cmderror : function(cmd) {
26300 if (this.text !== '\u0000') this.emitText()
26306 parseText : function(c)
26309 this.parserState = this.parseEscapes;
26310 } else if (c === '{') {
26311 this.emitStartGroup();
26312 } else if (c === '}') {
26313 this.emitEndGroup();
26314 } else if (c === '\x0A' || c === '\x0D') {
26315 // cr/lf are noise chars
26321 parseEscapes: function (c)
26323 if (c === '\\' || c === '{' || c === '}') {
26325 this.parserState = this.parseText;
26327 this.parserState = this.parseControlSymbol;
26328 this.parseControlSymbol(c);
26331 parseControlSymbol: function(c)
26334 this.text += '\u00a0'; // nbsp
26335 this.parserState = this.parseText
26336 } else if (c === '-') {
26337 this.text += '\u00ad'; // soft hyphen
26338 } else if (c === '_') {
26339 this.text += '\u2011'; // non-breaking hyphen
26340 } else if (c === '*') {
26341 this.emitIgnorable();
26342 this.parserState = this.parseText;
26343 } else if (c === "'") {
26344 this.parserState = this.parseHexChar;
26345 } else if (c === '|') { // formula cacter
26346 this.emitFormula();
26347 this.parserState = this.parseText;
26348 } else if (c === ':') { // subentry in an index entry
26349 this.emitIndexSubEntry();
26350 this.parserState = this.parseText;
26351 } else if (c === '\x0a') {
26352 this.emitEndParagraph();
26353 this.parserState = this.parseText;
26354 } else if (c === '\x0d') {
26355 this.emitEndParagraph();
26356 this.parserState = this.parseText;
26358 this.parserState = this.parseControlWord;
26359 this.parseControlWord(c);
26362 parseHexChar: function (c)
26364 if (/^[A-Fa-f0-9]$/.test(c)) {
26366 if (this.hexChar.length >= 2) {
26367 this.emitHexChar();
26368 this.parserState = this.parseText;
26372 this.emitError("Invalid character \"" + c + "\" in hex literal.");
26373 this.parserState = this.parseText;
26376 parseControlWord : function(c)
26379 this.emitControlWord();
26380 this.parserState = this.parseText;
26381 } else if (/^[-\d]$/.test(c)) {
26382 this.parserState = this.parseControlWordParam;
26383 this.controlWordParam += c;
26384 } else if (/^[A-Za-z]$/.test(c)) {
26385 this.controlWord += c;
26387 this.emitControlWord();
26388 this.parserState = this.parseText;
26392 parseControlWordParam : function (c) {
26393 if (/^\d$/.test(c)) {
26394 this.controlWordParam += c;
26395 } else if (c === ' ') {
26396 this.emitControlWord();
26397 this.parserState = this.parseText;
26399 this.emitControlWord();
26400 this.parserState = this.parseText;
26408 emitText : function () {
26409 if (this.text === '') {
26421 emitControlWord : function ()
26424 if (this.controlWord === '') {
26425 // do we want to track this - it seems just to cause problems.
26426 //this.emitError('empty control word');
26429 type: 'controlword',
26430 value: this.controlWord,
26431 param: this.controlWordParam !== '' && Number(this.controlWordParam),
26437 this.controlWord = '';
26438 this.controlWordParam = '';
26440 emitStartGroup : function ()
26444 type: 'groupstart',
26450 emitEndGroup : function ()
26460 emitIgnorable : function ()
26470 emitHexChar : function ()
26475 value: this.hexChar,
26482 emitError : function (message)
26490 char: this.cpos //,
26491 //stack: new Error().stack
26494 emitEndParagraph : function () {
26497 type: 'endparagraph',
26506 * @class Roo.htmleditor.Filter
26507 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26508 * @cfg {DomElement} node The node to iterate and filter
26509 * @cfg {boolean|String|Array} tag Tags to replace
26511 * Create a new Filter.
26512 * @param {Object} config Configuration options
26517 Roo.htmleditor.Filter = function(cfg) {
26518 Roo.apply(this.cfg);
26519 // this does not actually call walk as it's really just a abstract class
26523 Roo.htmleditor.Filter.prototype = {
26529 // overrride to do replace comments.
26530 replaceComment : false,
26532 // overrride to do replace or do stuff with tags..
26533 replaceTag : false,
26535 walk : function(dom)
26537 Roo.each( Array.from(dom.childNodes), function( e ) {
26540 case e.nodeType == 8 && this.replaceComment !== false: // comment
26541 this.replaceComment(e);
26544 case e.nodeType != 1: //not a node.
26547 case this.tag === true: // everything
26548 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26549 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26550 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26551 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26552 if (this.replaceTag && false === this.replaceTag(e)) {
26555 if (e.hasChildNodes()) {
26560 default: // tags .. that do not match.
26561 if (e.hasChildNodes()) {
26571 removeNodeKeepChildren : function( node)
26574 ar = Array.from(node.childNodes);
26575 for (var i = 0; i < ar.length; i++) {
26577 node.removeChild(ar[i]);
26578 // what if we need to walk these???
26579 node.parentNode.insertBefore(ar[i], node);
26582 node.parentNode.removeChild(node);
26587 * @class Roo.htmleditor.FilterAttributes
26588 * clean attributes and styles including http:// etc.. in attribute
26590 * Run a new Attribute Filter
26591 * @param {Object} config Configuration options
26593 Roo.htmleditor.FilterAttributes = function(cfg)
26595 Roo.apply(this, cfg);
26596 this.attrib_black = this.attrib_black || [];
26597 this.attrib_white = this.attrib_white || [];
26599 this.attrib_clean = this.attrib_clean || [];
26600 this.style_white = this.style_white || [];
26601 this.style_black = this.style_black || [];
26602 this.walk(cfg.node);
26605 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26607 tag: true, // all tags
26609 attrib_black : false, // array
26610 attrib_clean : false,
26611 attrib_white : false,
26613 style_white : false,
26614 style_black : false,
26617 replaceTag : function(node)
26619 if (!node.attributes || !node.attributes.length) {
26623 for (var i = node.attributes.length-1; i > -1 ; i--) {
26624 var a = node.attributes[i];
26626 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26627 node.removeAttribute(a.name);
26633 if (a.name.toLowerCase().substr(0,2)=='on') {
26634 node.removeAttribute(a.name);
26639 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26640 node.removeAttribute(a.name);
26643 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26644 this.cleanAttr(node,a.name,a.value); // fixme..
26647 if (a.name == 'style') {
26648 this.cleanStyle(node,a.name,a.value);
26651 /// clean up MS crap..
26652 // tecnically this should be a list of valid class'es..
26655 if (a.name == 'class') {
26656 if (a.value.match(/^Mso/)) {
26657 node.removeAttribute('class');
26660 if (a.value.match(/^body$/)) {
26661 node.removeAttribute('class');
26671 return true; // clean children
26674 cleanAttr: function(node, n,v)
26677 if (v.match(/^\./) || v.match(/^\//)) {
26680 if (v.match(/^(http|https):\/\//)
26681 || v.match(/^mailto:/)
26682 || v.match(/^ftp:/)
26683 || v.match(/^data:/)
26687 if (v.match(/^#/)) {
26690 if (v.match(/^\{/)) { // allow template editing.
26693 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26694 node.removeAttribute(n);
26697 cleanStyle : function(node, n,v)
26699 if (v.match(/expression/)) { //XSS?? should we even bother..
26700 node.removeAttribute(n);
26704 var parts = v.split(/;/);
26707 Roo.each(parts, function(p) {
26708 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26712 var l = p.split(':').shift().replace(/\s+/g,'');
26713 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26715 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26719 // only allow 'c whitelisted system attributes'
26720 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26728 if (clean.length) {
26729 node.setAttribute(n, clean.join(';'));
26731 node.removeAttribute(n);
26740 * @class Roo.htmleditor.FilterBlack
26741 * remove blacklisted elements.
26743 * Run a new Blacklisted Filter
26744 * @param {Object} config Configuration options
26747 Roo.htmleditor.FilterBlack = function(cfg)
26749 Roo.apply(this, cfg);
26750 this.walk(cfg.node);
26753 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26755 tag : true, // all elements.
26757 replaceTag : function(n)
26759 n.parentNode.removeChild(n);
26763 * @class Roo.htmleditor.FilterComment
26766 * Run a new Comments Filter
26767 * @param {Object} config Configuration options
26769 Roo.htmleditor.FilterComment = function(cfg)
26771 this.walk(cfg.node);
26774 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26777 replaceComment : function(n)
26779 n.parentNode.removeChild(n);
26782 * @class Roo.htmleditor.FilterKeepChildren
26783 * remove tags but keep children
26785 * Run a new Keep Children Filter
26786 * @param {Object} config Configuration options
26789 Roo.htmleditor.FilterKeepChildren = function(cfg)
26791 Roo.apply(this, cfg);
26792 if (this.tag === false) {
26793 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26796 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26797 this.cleanNamespace = true;
26800 this.walk(cfg.node);
26803 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26805 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26807 replaceTag : function(node)
26809 // walk children...
26810 //Roo.log(node.tagName);
26811 var ar = Array.from(node.childNodes);
26814 for (var i = 0; i < ar.length; i++) {
26816 if (e.nodeType == 1) {
26818 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26819 || // array and it matches
26820 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26822 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26824 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26826 this.replaceTag(ar[i]); // child is blacklisted as well...
26831 ar = Array.from(node.childNodes);
26832 for (var i = 0; i < ar.length; i++) {
26834 node.removeChild(ar[i]);
26835 // what if we need to walk these???
26836 node.parentNode.insertBefore(ar[i], node);
26837 if (this.tag !== false) {
26842 //Roo.log("REMOVE:" + node.tagName);
26843 node.parentNode.removeChild(node);
26844 return false; // don't walk children
26849 * @class Roo.htmleditor.FilterParagraph
26850 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26851 * like on 'push' to remove the <p> tags and replace them with line breaks.
26853 * Run a new Paragraph Filter
26854 * @param {Object} config Configuration options
26857 Roo.htmleditor.FilterParagraph = function(cfg)
26859 // no need to apply config.
26860 this.walk(cfg.node);
26863 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26870 replaceTag : function(node)
26873 if (node.childNodes.length == 1 &&
26874 node.childNodes[0].nodeType == 3 &&
26875 node.childNodes[0].textContent.trim().length < 1
26877 // remove and replace with '<BR>';
26878 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26879 return false; // no need to walk..
26881 var ar = Array.from(node.childNodes);
26882 for (var i = 0; i < ar.length; i++) {
26883 node.removeChild(ar[i]);
26884 // what if we need to walk these???
26885 node.parentNode.insertBefore(ar[i], node);
26887 // now what about this?
26891 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26892 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26893 node.parentNode.removeChild(node);
26900 * @class Roo.htmleditor.FilterSpan
26901 * filter span's with no attributes out..
26903 * Run a new Span Filter
26904 * @param {Object} config Configuration options
26907 Roo.htmleditor.FilterSpan = function(cfg)
26909 // no need to apply config.
26910 this.walk(cfg.node);
26913 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26919 replaceTag : function(node)
26921 if (node.attributes && node.attributes.length > 0) {
26922 return true; // walk if there are any.
26924 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26930 * @class Roo.htmleditor.FilterTableWidth
26931 try and remove table width data - as that frequently messes up other stuff.
26933 * was cleanTableWidths.
26935 * Quite often pasting from word etc.. results in tables with column and widths.
26936 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26939 * Run a new Table Filter
26940 * @param {Object} config Configuration options
26943 Roo.htmleditor.FilterTableWidth = function(cfg)
26945 // no need to apply config.
26946 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26947 this.walk(cfg.node);
26950 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26955 replaceTag: function(node) {
26959 if (node.hasAttribute('width')) {
26960 node.removeAttribute('width');
26964 if (node.hasAttribute("style")) {
26967 var styles = node.getAttribute("style").split(";");
26969 Roo.each(styles, function(s) {
26970 if (!s.match(/:/)) {
26973 var kv = s.split(":");
26974 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26977 // what ever is left... we allow.
26980 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26981 if (!nstyle.length) {
26982 node.removeAttribute('style');
26986 return true; // continue doing children..
26989 * @class Roo.htmleditor.FilterWord
26990 * try and clean up all the mess that Word generates.
26992 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26995 * Run a new Span Filter
26996 * @param {Object} config Configuration options
26999 Roo.htmleditor.FilterWord = function(cfg)
27001 // no need to apply config.
27002 this.replaceDocBullets(cfg.node);
27004 this.replaceAname(cfg.node);
27005 // this is disabled as the removal is done by other filters;
27006 // this.walk(cfg.node);
27007 this.replaceImageTable(cfg.node);
27011 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27017 * Clean up MS wordisms...
27019 replaceTag : function(node)
27022 // no idea what this does - span with text, replaceds with just text.
27024 node.nodeName == 'SPAN' &&
27025 !node.hasAttributes() &&
27026 node.childNodes.length == 1 &&
27027 node.firstChild.nodeName == "#text"
27029 var textNode = node.firstChild;
27030 node.removeChild(textNode);
27031 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27032 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27034 node.parentNode.insertBefore(textNode, node);
27035 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27036 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27039 node.parentNode.removeChild(node);
27040 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27045 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27046 node.parentNode.removeChild(node);
27047 return false; // dont do chidlren
27049 //Roo.log(node.tagName);
27050 // remove - but keep children..
27051 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27052 //Roo.log('-- removed');
27053 while (node.childNodes.length) {
27054 var cn = node.childNodes[0];
27055 node.removeChild(cn);
27056 node.parentNode.insertBefore(cn, node);
27057 // move node to parent - and clean it..
27058 if (cn.nodeType == 1) {
27059 this.replaceTag(cn);
27063 node.parentNode.removeChild(node);
27064 /// no need to iterate chidlren = it's got none..
27065 //this.iterateChildren(node, this.cleanWord);
27066 return false; // no need to iterate children.
27069 if (node.className.length) {
27071 var cn = node.className.split(/\W+/);
27073 Roo.each(cn, function(cls) {
27074 if (cls.match(/Mso[a-zA-Z]+/)) {
27079 node.className = cna.length ? cna.join(' ') : '';
27081 node.removeAttribute("class");
27085 if (node.hasAttribute("lang")) {
27086 node.removeAttribute("lang");
27089 if (node.hasAttribute("style")) {
27091 var styles = node.getAttribute("style").split(";");
27093 Roo.each(styles, function(s) {
27094 if (!s.match(/:/)) {
27097 var kv = s.split(":");
27098 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27101 // what ever is left... we allow.
27104 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27105 if (!nstyle.length) {
27106 node.removeAttribute('style');
27109 return true; // do children
27115 styleToObject: function(node)
27117 var styles = (node.getAttribute("style") || '').split(";");
27119 Roo.each(styles, function(s) {
27120 if (!s.match(/:/)) {
27123 var kv = s.split(":");
27125 // what ever is left... we allow.
27126 ret[kv[0].trim()] = kv[1];
27132 replaceAname : function (doc)
27134 // replace all the a/name without..
27135 var aa = Array.from(doc.getElementsByTagName('a'));
27136 for (var i = 0; i < aa.length; i++) {
27138 if (a.hasAttribute("name")) {
27139 a.removeAttribute("name");
27141 if (a.hasAttribute("href")) {
27144 // reparent children.
27145 this.removeNodeKeepChildren(a);
27155 replaceDocBullets : function(doc)
27157 // this is a bit odd - but it appears some indents use ql-indent-1
27158 //Roo.log(doc.innerHTML);
27160 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27161 for( var i = 0; i < listpara.length; i ++) {
27162 listpara[i].className = "MsoListParagraph";
27165 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27166 for( var i = 0; i < listpara.length; i ++) {
27167 listpara[i].className = "MsoListParagraph";
27169 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27170 for( var i = 0; i < listpara.length; i ++) {
27171 listpara[i].className = "MsoListParagraph";
27173 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
27174 for( var i = 0; i < listpara.length; i ++) {
27175 listpara[i].className = "MsoListParagraph";
27178 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27179 var htwo = Array.from(doc.getElementsByTagName('h2'));
27180 for( var i = 0; i < htwo.length; i ++) {
27181 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27182 htwo[i].className = "MsoListParagraph";
27185 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
27186 for( var i = 0; i < listpara.length; i ++) {
27187 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27188 listpara[i].className = "MsoListParagraph";
27190 listpara[i].className = "MsoNormalx";
27194 listpara = doc.getElementsByClassName('MsoListParagraph');
27195 // Roo.log(doc.innerHTML);
27199 while(listpara.length) {
27201 this.replaceDocBullet(listpara.item(0));
27208 replaceDocBullet : function(p)
27210 // gather all the siblings.
27212 parent = p.parentNode,
27213 doc = parent.ownerDocument,
27216 //Roo.log("Parsing: " + p.innerText) ;
27217 var listtype = 'ul';
27219 if (ns.nodeType != 1) {
27220 ns = ns.nextSibling;
27223 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27224 //Roo.log("Missing para r q1indent - got:" + ns.className);
27227 var spans = ns.getElementsByTagName('span');
27229 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27231 ns = ns.nextSibling;
27233 if (!spans.length) {
27238 for (var i = 0; i < spans.length;i++) {
27240 if (se.hasAttribute('style') && se.hasAttribute('style') && se.style.fontFamily != '') {
27241 ff = se.style.fontFamily;
27247 //Roo.log("got font family: " + ff);
27248 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27254 //Roo.log("no mso-list?");
27256 var spans = ns.getElementsByTagName('span');
27257 if (!spans.length) {
27260 var has_list = false;
27261 for(var i = 0; i < spans.length; i++) {
27262 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27271 ns = ns.nextSibling;
27275 if (!items.length) {
27280 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27281 parent.insertBefore(ul, p);
27283 var stack = [ ul ];
27284 var last_li = false;
27286 var margin_to_depth = {};
27289 items.forEach(function(n, ipos) {
27290 //Roo.log("got innertHMLT=" + n.innerHTML);
27292 var spans = n.getElementsByTagName('span');
27293 if (!spans.length) {
27294 //Roo.log("No spans found");
27296 parent.removeChild(n);
27299 return; // skip it...
27305 for(var i = 0; i < spans.length; i++) {
27307 style = this.styleToObject(spans[i]);
27308 if (typeof(style['mso-list']) == 'undefined') {
27311 if (listtype == 'ol') {
27312 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
27314 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27317 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27318 style = this.styleToObject(n); // mo-list is from the parent node.
27319 if (typeof(style['mso-list']) == 'undefined') {
27320 //Roo.log("parent is missing level");
27322 parent.removeChild(n);
27327 var margin = style['margin-left'];
27328 if (typeof(margin_to_depth[margin]) == 'undefined') {
27330 margin_to_depth[margin] = max_margins;
27332 nlvl = margin_to_depth[margin] ;
27336 var nul = doc.createElement(listtype); // what about number lists...
27338 last_li = doc.createElement('li');
27339 stack[lvl].appendChild(last_li);
27341 last_li.appendChild(nul);
27347 // not starting at 1..
27348 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27349 stack[nlvl].setAttribute("start", num);
27352 var nli = stack[nlvl].appendChild(doc.createElement('li'));
27354 nli.innerHTML = n.innerHTML;
27355 //Roo.log("innerHTML = " + n.innerHTML);
27356 parent.removeChild(n);
27368 replaceImageTable : function(doc)
27371 <table cellpadding=0 cellspacing=0 align=left>
27373 <td width=423 height=0></td>
27377 <td><img width=601 height=401
27378 src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27379 v:shapes="Picture_x0020_2"></td>
27383 var imgs = Array.from(doc.getElementsByTagName('img'));
27384 Roo.each(imgs, function(img) {
27385 var td = img.parentNode;
27386 if (td.nodeName != 'TD') {
27389 var tr = td.parentNode;
27390 if (tr.nodeName != 'TR') {
27393 var tbody = tr.parentNode;
27394 if (tbody.nodeName != 'TBODY') {
27397 var table = tbody.parentNode;
27398 if (table.nodeName != 'TABLE') {
27403 if (table.getElementsByTagName('tr').length != 2) {
27406 if (table.getElementsByTagName('td').length != 3) {
27409 if (table.innerText.trim() != '') {
27412 var p = table.parentNode;
27413 img.parentNode.removeChild(img);
27414 p.insertBefore(img, table);
27415 p.removeChild(table);
27426 * @class Roo.htmleditor.FilterStyleToTag
27427 * part of the word stuff... - certain 'styles' should be converted to tags.
27429 * font-weight: bold -> bold
27430 * ?? super / subscrit etc..
27433 * Run a new style to tag filter.
27434 * @param {Object} config Configuration options
27436 Roo.htmleditor.FilterStyleToTag = function(cfg)
27440 B : [ 'fontWeight' , 'bold'],
27441 I : [ 'fontStyle' , 'italic'],
27442 //pre : [ 'font-style' , 'italic'],
27443 // h1.. h6 ?? font-size?
27444 SUP : [ 'verticalAlign' , 'super' ],
27445 SUB : [ 'verticalAlign' , 'sub' ]
27450 Roo.apply(this, cfg);
27453 this.walk(cfg.node);
27460 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27462 tag: true, // all tags
27467 replaceTag : function(node)
27471 if (node.getAttribute("style") === null) {
27475 for (var k in this.tags) {
27476 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27478 node.style.removeProperty(this.tags[k][0]);
27481 if (!inject.length) {
27484 var cn = Array.from(node.childNodes);
27486 Roo.each(inject, function(t) {
27487 var nc = node.ownerDocument.createElement(t);
27488 nn.appendChild(nc);
27491 for(var i = 0;i < cn.length;cn++) {
27492 node.removeChild(cn[i]);
27493 nn.appendChild(cn[i]);
27495 return true /// iterate thru
27499 * @class Roo.htmleditor.FilterLongBr
27500 * BR/BR/BR - keep a maximum of 2...
27502 * Run a new Long BR Filter
27503 * @param {Object} config Configuration options
27506 Roo.htmleditor.FilterLongBr = function(cfg)
27508 // no need to apply config.
27509 this.walk(cfg.node);
27512 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27519 replaceTag : function(node)
27522 var ps = node.nextSibling;
27523 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27524 ps = ps.nextSibling;
27527 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
27528 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27532 if (!ps || ps.nodeType != 1) {
27536 if (!ps || ps.tagName != 'BR') {
27545 if (!node.previousSibling) {
27548 var ps = node.previousSibling;
27550 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27551 ps = ps.previousSibling;
27553 if (!ps || ps.nodeType != 1) {
27556 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27557 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27561 node.parentNode.removeChild(node); // remove me...
27563 return false; // no need to do children
27570 * @class Roo.htmleditor.FilterBlock
27571 * removes id / data-block and contenteditable that are associated with blocks
27572 * usage should be done on a cloned copy of the dom
27574 * Run a new Attribute Filter { node : xxxx }}
27575 * @param {Object} config Configuration options
27577 Roo.htmleditor.FilterBlock = function(cfg)
27579 Roo.apply(this, cfg);
27580 var qa = cfg.node.querySelectorAll;
27581 this.removeAttributes('data-block');
27582 this.removeAttributes('contenteditable');
27583 this.removeAttributes('id');
27587 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27589 node: true, // all tags
27592 removeAttributes : function(attr)
27594 var ar = this.node.querySelectorAll('*[' + attr + ']');
27595 for (var i =0;i<ar.length;i++) {
27596 ar[i].removeAttribute(attr);
27605 * This is based loosely on tinymce
27606 * @class Roo.htmleditor.TidySerializer
27607 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27609 * @method Serializer
27610 * @param {Object} settings Name/value settings object.
27614 Roo.htmleditor.TidySerializer = function(settings)
27616 Roo.apply(this, settings);
27618 this.writer = new Roo.htmleditor.TidyWriter(settings);
27623 Roo.htmleditor.TidySerializer.prototype = {
27626 * @param {boolean} inner do the inner of the node.
27633 * Serializes the specified node into a string.
27636 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27637 * @method serialize
27638 * @param {DomElement} node Node instance to serialize.
27639 * @return {String} String with HTML based on DOM tree.
27641 serialize : function(node) {
27643 // = settings.validate;
27644 var writer = this.writer;
27648 3: function(node) {
27650 writer.text(node.nodeValue, node);
27653 8: function(node) {
27654 writer.comment(node.nodeValue);
27656 // Processing instruction
27657 7: function(node) {
27658 writer.pi(node.name, node.nodeValue);
27661 10: function(node) {
27662 writer.doctype(node.nodeValue);
27665 4: function(node) {
27666 writer.cdata(node.nodeValue);
27668 // Document fragment
27669 11: function(node) {
27670 node = node.firstChild;
27676 node = node.nextSibling
27681 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27682 return writer.getContent();
27685 walk: function(node)
27687 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27688 handler = this.handlers[node.nodeType];
27695 var name = node.nodeName;
27696 var isEmpty = node.childNodes.length < 1;
27698 var writer = this.writer;
27699 var attrs = node.attributes;
27702 writer.start(node.nodeName, attrs, isEmpty, node);
27706 node = node.firstChild;
27713 node = node.nextSibling;
27719 // Serialize element and treat all non elements as fragments
27724 * This is based loosely on tinymce
27725 * @class Roo.htmleditor.TidyWriter
27726 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27729 * - not tested much with 'PRE' formated elements.
27735 Roo.htmleditor.TidyWriter = function(settings)
27738 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27739 Roo.apply(this, settings);
27743 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27746 Roo.htmleditor.TidyWriter.prototype = {
27753 // part of state...
27757 last_inline : false,
27762 * Writes the a start element such as <p id="a">.
27765 * @param {String} name Name of the element.
27766 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27767 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27769 start: function(name, attrs, empty, node)
27771 var i, l, attr, value;
27773 // there are some situations where adding line break && indentation will not work. will not work.
27774 // <span / b / i ... formating?
27776 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27777 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27779 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27781 var add_lb = name == 'BR' ? false : in_inline;
27783 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27787 var indentstr = this.indentstr;
27789 // e_inline = elements that can be inline, but still allow \n before and after?
27790 // only 'BR' ??? any others?
27792 // ADD LINE BEFORE tage
27793 if (!this.in_pre) {
27796 if (name == 'BR') {
27798 } else if (this.lastElementEndsWS()) {
27801 // otherwise - no new line. (and dont indent.)
27812 this.html.push(indentstr + '<', name.toLowerCase());
27815 for (i = 0, l = attrs.length; i < l; i++) {
27817 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27823 this.html[this.html.length] = '/>';
27825 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27827 var e_inline = name == 'BR' ? false : this.in_inline;
27829 if (!e_inline && !this.in_pre) {
27836 this.html[this.html.length] = '>';
27838 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27840 if (!in_inline && !in_pre) {
27841 var cn = node.firstChild;
27843 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27847 cn = cn.nextSibling;
27855 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27857 in_inline : in_inline
27859 // add a line after if we are not in a
27861 if (!in_inline && !in_pre) {
27870 lastElementEndsWS : function()
27872 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27873 if (value === false) {
27876 return value.match(/\s+$/);
27881 * Writes the a end element such as </p>.
27884 * @param {String} name Name of the element.
27886 end: function(name) {
27889 var indentstr = '';
27890 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27892 if (!this.in_pre && !in_inline) {
27894 indentstr = this.indentstr;
27896 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27897 this.last_inline = in_inline;
27899 // pop the indent state..
27902 * Writes a text node.
27904 * In pre - we should not mess with the contents.
27908 * @param {String} text String to write out.
27909 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27911 text: function(in_text, node)
27913 // if not in whitespace critical
27914 if (in_text.length < 1) {
27917 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27920 this.html[this.html.length] = text;
27924 if (this.in_inline) {
27925 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27927 text = text.replace(/\s+/,' '); // all white space to single white space
27930 // if next tag is '<BR>', then we can trim right..
27931 if (node.nextSibling &&
27932 node.nextSibling.nodeType == 1 &&
27933 node.nextSibling.nodeName == 'BR' )
27935 text = text.replace(/\s+$/g,'');
27937 // if previous tag was a BR, we can also trim..
27938 if (node.previousSibling &&
27939 node.previousSibling.nodeType == 1 &&
27940 node.previousSibling.nodeName == 'BR' )
27942 text = this.indentstr + text.replace(/^\s+/g,'');
27944 if (text.match(/\n/)) {
27945 text = text.replace(
27946 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27948 // remoeve the last whitespace / line break.
27949 text = text.replace(/\n\s+$/,'');
27951 // repace long lines
27955 this.html[this.html.length] = text;
27958 // see if previous element was a inline element.
27959 var indentstr = this.indentstr;
27961 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27963 // should trim left?
27964 if (node.previousSibling &&
27965 node.previousSibling.nodeType == 1 &&
27966 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27972 text = text.replace(/^\s+/,''); // trim left
27975 // should trim right?
27976 if (node.nextSibling &&
27977 node.nextSibling.nodeType == 1 &&
27978 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27983 text = text.replace(/\s+$/,''); // trim right
27990 if (text.length < 1) {
27993 if (!text.match(/\n/)) {
27994 this.html.push(indentstr + text);
27998 text = this.indentstr + text.replace(
27999 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28001 // remoeve the last whitespace / line break.
28002 text = text.replace(/\s+$/,'');
28004 this.html.push(text);
28006 // split and indent..
28011 * Writes a cdata node such as <![CDATA[data]]>.
28014 * @param {String} text String to write out inside the cdata.
28016 cdata: function(text) {
28017 this.html.push('<![CDATA[', text, ']]>');
28020 * Writes a comment node such as <!-- Comment -->.
28023 * @param {String} text String to write out inside the comment.
28025 comment: function(text) {
28026 this.html.push('<!--', text, '-->');
28029 * Writes a PI node such as <?xml attr="value" ?>.
28032 * @param {String} name Name of the pi.
28033 * @param {String} text String to write out inside the pi.
28035 pi: function(name, text) {
28036 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28037 this.indent != '' && this.html.push('\n');
28040 * Writes a doctype node such as <!DOCTYPE data>.
28043 * @param {String} text String to write out inside the doctype.
28045 doctype: function(text) {
28046 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28049 * Resets the internal buffer if one wants to reuse the writer.
28053 reset: function() {
28054 this.html.length = 0;
28063 * Returns the contents that got serialized.
28065 * @method getContent
28066 * @return {String} HTML contents that got written down.
28068 getContent: function() {
28069 return this.html.join('').replace(/\n$/, '');
28072 pushState : function(cfg)
28074 this.state.push(cfg);
28075 Roo.apply(this, cfg);
28078 popState : function()
28080 if (this.state.length < 1) {
28081 return; // nothing to push
28088 if (this.state.length > 0) {
28089 cfg = this.state[this.state.length-1];
28091 Roo.apply(this, cfg);
28094 addLine: function()
28096 if (this.html.length < 1) {
28101 var value = this.html[this.html.length - 1];
28102 if (value.length > 0 && '\n' !== value) {
28103 this.html.push('\n');
28108 //'pre script noscript style textarea video audio iframe object code'
28109 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
28113 Roo.htmleditor.TidyWriter.inline_elements = [
28114 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28115 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28117 Roo.htmleditor.TidyWriter.shortend_elements = [
28118 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28119 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28122 Roo.htmleditor.TidyWriter.whitespace_elements = [
28123 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28125 * This is based loosely on tinymce
28126 * @class Roo.htmleditor.TidyEntities
28128 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28130 * Not 100% sure this is actually used or needed.
28133 Roo.htmleditor.TidyEntities = {
28136 * initialize data..
28138 init : function (){
28140 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28145 buildEntitiesLookup: function(items, radix) {
28146 var i, chr, entity, lookup = {};
28150 items = typeof(items) == 'string' ? items.split(',') : items;
28151 radix = radix || 10;
28152 // Build entities lookup table
28153 for (i = 0; i < items.length; i += 2) {
28154 chr = String.fromCharCode(parseInt(items[i], radix));
28155 // Only add non base entities
28156 if (!this.baseEntities[chr]) {
28157 entity = '&' + items[i + 1] + ';';
28158 lookup[chr] = entity;
28159 lookup[entity] = chr;
28198 // Needs to be escaped since the YUI compressor would otherwise break the code
28205 // Reverse lookup table for raw entities
28206 reverseEntities : {
28214 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28215 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28216 rawCharsRegExp : /[<>&\"\']/g,
28217 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28218 namedEntities : false,
28219 namedEntitiesData : [
28720 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28722 * @method encodeRaw
28723 * @param {String} text Text to encode.
28724 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28725 * @return {String} Entity encoded text.
28727 encodeRaw: function(text, attr)
28730 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28731 return t.baseEntities[chr] || chr;
28735 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28736 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28737 * and is exposed as the DOMUtils.encode function.
28739 * @method encodeAllRaw
28740 * @param {String} text Text to encode.
28741 * @return {String} Entity encoded text.
28743 encodeAllRaw: function(text) {
28745 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28746 return t.baseEntities[chr] || chr;
28750 * Encodes the specified string using numeric entities. The core entities will be
28751 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28753 * @method encodeNumeric
28754 * @param {String} text Text to encode.
28755 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28756 * @return {String} Entity encoded text.
28758 encodeNumeric: function(text, attr) {
28760 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28761 // Multi byte sequence convert it to a single entity
28762 if (chr.length > 1) {
28763 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28765 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28769 * Encodes the specified string using named entities. The core entities will be encoded
28770 * as named ones but all non lower ascii characters will be encoded into named entities.
28772 * @method encodeNamed
28773 * @param {String} text Text to encode.
28774 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28775 * @param {Object} entities Optional parameter with entities to use.
28776 * @return {String} Entity encoded text.
28778 encodeNamed: function(text, attr, entities) {
28780 entities = entities || this.namedEntities;
28781 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28782 return t.baseEntities[chr] || entities[chr] || chr;
28786 * Returns an encode function based on the name(s) and it's optional entities.
28788 * @method getEncodeFunc
28789 * @param {String} name Comma separated list of encoders for example named,numeric.
28790 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28791 * @return {function} Encode function to be used.
28793 getEncodeFunc: function(name, entities) {
28794 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28796 function encodeNamedAndNumeric(text, attr) {
28797 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28798 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28802 function encodeCustomNamed(text, attr) {
28803 return t.encodeNamed(text, attr, entities);
28805 // Replace + with , to be compatible with previous TinyMCE versions
28806 name = this.makeMap(name.replace(/\+/g, ','));
28807 // Named and numeric encoder
28808 if (name.named && name.numeric) {
28809 return this.encodeNamedAndNumeric;
28815 return encodeCustomNamed;
28817 return this.encodeNamed;
28820 if (name.numeric) {
28821 return this.encodeNumeric;
28824 return this.encodeRaw;
28827 * Decodes the specified string, this will replace entities with raw UTF characters.
28830 * @param {String} text Text to entity decode.
28831 * @return {String} Entity decoded string.
28833 decode: function(text)
28836 return text.replace(this.entityRegExp, function(all, numeric) {
28838 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28839 // Support upper UTF
28840 if (numeric > 65535) {
28842 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28844 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28846 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28849 nativeDecode : function (text) {
28852 makeMap : function (items, delim, map) {
28854 items = items || [];
28855 delim = delim || ',';
28856 if (typeof items == "string") {
28857 items = items.split(delim);
28862 map[items[i]] = {};
28870 Roo.htmleditor.TidyEntities.init();
28872 * @class Roo.htmleditor.KeyEnter
28873 * Handle Enter press..
28874 * @cfg {Roo.HtmlEditorCore} core the editor.
28876 * Create a new Filter.
28877 * @param {Object} config Configuration options
28884 Roo.htmleditor.KeyEnter = function(cfg) {
28885 Roo.apply(this, cfg);
28886 // this does not actually call walk as it's really just a abstract class
28888 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28891 //Roo.htmleditor.KeyEnter.i = 0;
28894 Roo.htmleditor.KeyEnter.prototype = {
28898 keypress : function(e)
28900 if (e.charCode != 13 && e.charCode != 10) {
28901 Roo.log([e.charCode,e]);
28904 e.preventDefault();
28905 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28906 var doc = this.core.doc;
28910 var sel = this.core.getSelection();
28911 var range = sel.getRangeAt(0);
28912 var n = range.commonAncestorContainer;
28913 var pc = range.closest([ 'ol', 'ul']);
28914 var pli = range.closest('li');
28915 if (!pc || e.ctrlKey) {
28916 // on it list, or ctrl pressed.
28918 sel.insertNode('br', 'after');
28920 // only do this if we have ctrl key..
28921 var br = doc.createElement('br');
28922 br.className = 'clear';
28923 br.setAttribute('style', 'clear: both');
28924 sel.insertNode(br, 'after');
28928 this.core.undoManager.addEvent();
28929 this.core.fireEditorEvent(e);
28933 // deal with <li> insetion
28934 if (pli.innerText.trim() == '' &&
28935 pli.previousSibling &&
28936 pli.previousSibling.nodeName == 'LI' &&
28937 pli.previousSibling.innerText.trim() == '') {
28938 pli.parentNode.removeChild(pli.previousSibling);
28939 sel.cursorAfter(pc);
28940 this.core.undoManager.addEvent();
28941 this.core.fireEditorEvent(e);
28945 var li = doc.createElement('LI');
28946 li.innerHTML = ' ';
28947 if (!pli || !pli.firstSibling) {
28948 pc.appendChild(li);
28950 pli.parentNode.insertBefore(li, pli.firstSibling);
28952 sel.cursorText (li.firstChild);
28954 this.core.undoManager.addEvent();
28955 this.core.fireEditorEvent(e);
28967 * @class Roo.htmleditor.Block
28968 * Base class for html editor blocks - do not use it directly .. extend it..
28969 * @cfg {DomElement} node The node to apply stuff to.
28970 * @cfg {String} friendly_name the name that appears in the context bar about this block
28971 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28974 * Create a new Filter.
28975 * @param {Object} config Configuration options
28978 Roo.htmleditor.Block = function(cfg)
28980 // do nothing .. should not be called really.
28983 * factory method to get the block from an element (using cache if necessary)
28985 * @param {HtmlElement} the dom element
28987 Roo.htmleditor.Block.factory = function(node)
28989 var cc = Roo.htmleditor.Block.cache;
28990 var id = Roo.get(node).id;
28991 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28992 Roo.htmleditor.Block.cache[id].readElement(node);
28993 return Roo.htmleditor.Block.cache[id];
28995 var db = node.getAttribute('data-block');
28997 db = node.nodeName.toLowerCase().toUpperCaseFirst();
28999 var cls = Roo.htmleditor['Block' + db];
29000 if (typeof(cls) == 'undefined') {
29001 //Roo.log(node.getAttribute('data-block'));
29002 Roo.log("OOps missing block : " + 'Block' + db);
29005 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29006 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
29010 * initalize all Elements from content that are 'blockable'
29012 * @param the body element
29014 Roo.htmleditor.Block.initAll = function(body, type)
29016 if (typeof(type) == 'undefined') {
29017 var ia = Roo.htmleditor.Block.initAll;
29023 Roo.each(Roo.get(body).query(type), function(e) {
29024 Roo.htmleditor.Block.factory(e);
29027 // question goes here... do we need to clear out this cache sometimes?
29028 // or show we make it relivant to the htmleditor.
29029 Roo.htmleditor.Block.cache = {};
29031 Roo.htmleditor.Block.prototype = {
29035 // used by context menu
29036 friendly_name : 'Based Block',
29038 // text for button to delete this element
29039 deleteTitle : false,
29043 * Update a node with values from this object
29044 * @param {DomElement} node
29046 updateElement : function(node)
29048 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29051 * convert to plain HTML for calling insertAtCursor..
29053 toHTML : function()
29055 return Roo.DomHelper.markup(this.toObject());
29058 * used by readEleemnt to extract data from a node
29059 * may need improving as it's pretty basic
29061 * @param {DomElement} node
29062 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29063 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29064 * @param {String} style the style property - eg. text-align
29066 getVal : function(node, tag, attr, style)
29069 if (tag !== true && n.tagName != tag.toUpperCase()) {
29070 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29071 // but kiss for now.
29072 n = node.getElementsByTagName(tag).item(0);
29077 if (attr === false) {
29080 if (attr == 'html') {
29081 return n.innerHTML;
29083 if (attr == 'style') {
29084 return n.style[style];
29087 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29091 * create a DomHelper friendly object - for use with
29092 * Roo.DomHelper.markup / overwrite / etc..
29095 toObject : function()
29100 * Read a node that has a 'data-block' property - and extract the values from it.
29101 * @param {DomElement} node - the node
29103 readElement : function(node)
29114 * @class Roo.htmleditor.BlockFigure
29115 * Block that has an image and a figcaption
29116 * @cfg {String} image_src the url for the image
29117 * @cfg {String} align (left|right) alignment for the block default left
29118 * @cfg {String} caption the text to appear below (and in the alt tag)
29119 * @cfg {String} caption_display (block|none) display or not the caption
29120 * @cfg {String|number} image_width the width of the image number or %?
29121 * @cfg {String|number} image_height the height of the image number or %?
29124 * Create a new Filter.
29125 * @param {Object} config Configuration options
29128 Roo.htmleditor.BlockFigure = function(cfg)
29131 this.readElement(cfg.node);
29132 this.updateElement(cfg.node);
29134 Roo.apply(this, cfg);
29136 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29143 caption_display : 'block',
29149 // margin: '2%', not used
29151 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
29154 // used by context menu
29155 friendly_name : 'Image with caption',
29156 deleteTitle : "Delete Image and Caption",
29158 contextMenu : function(toolbar)
29161 var block = function() {
29162 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29166 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29168 var syncValue = toolbar.editorcore.syncValue;
29174 xtype : 'TextItem',
29176 xns : rooui.Toolbar //Boostrap?
29180 text: 'Change Image URL',
29183 click: function (btn, state)
29187 Roo.MessageBox.show({
29188 title : "Image Source URL",
29189 msg : "Enter the url for the image",
29190 buttons: Roo.MessageBox.OKCANCEL,
29191 fn: function(btn, val){
29198 toolbar.editorcore.onEditorEvent();
29202 //multiline: multiline,
29204 value : b.image_src
29208 xns : rooui.Toolbar
29213 text: 'Change Link URL',
29216 click: function (btn, state)
29220 Roo.MessageBox.show({
29221 title : "Link URL",
29222 msg : "Enter the url for the link - leave blank to have no link",
29223 buttons: Roo.MessageBox.OKCANCEL,
29224 fn: function(btn, val){
29231 toolbar.editorcore.onEditorEvent();
29235 //multiline: multiline,
29241 xns : rooui.Toolbar
29245 text: 'Show Video URL',
29248 click: function (btn, state)
29250 Roo.MessageBox.alert("Video URL",
29251 block().video_url == '' ? 'This image is not linked ot a video' :
29252 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29255 xns : rooui.Toolbar
29260 xtype : 'TextItem',
29262 xns : rooui.Toolbar //Boostrap?
29265 xtype : 'ComboBox',
29266 allowBlank : false,
29267 displayField : 'val',
29270 triggerAction : 'all',
29272 valueField : 'val',
29276 select : function (combo, r, index)
29278 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29280 b.width = r.get('val');
29283 toolbar.editorcore.onEditorEvent();
29288 xtype : 'SimpleStore',
29301 xtype : 'TextItem',
29303 xns : rooui.Toolbar //Boostrap?
29306 xtype : 'ComboBox',
29307 allowBlank : false,
29308 displayField : 'val',
29311 triggerAction : 'all',
29313 valueField : 'val',
29317 select : function (combo, r, index)
29319 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29321 b.align = r.get('val');
29324 toolbar.editorcore.onEditorEvent();
29329 xtype : 'SimpleStore',
29343 text: 'Hide Caption',
29344 name : 'caption_display',
29346 enableToggle : true,
29347 setValue : function(v) {
29348 // this trigger toggle.
29350 this.setText(v ? "Hide Caption" : "Show Caption");
29351 this.setPressed(v != 'block');
29354 toggle: function (btn, state)
29357 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29358 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29361 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29362 toolbar.editorcore.onEditorEvent();
29365 xns : rooui.Toolbar
29371 * create a DomHelper friendly object - for use with
29372 * Roo.DomHelper.markup / overwrite / etc..
29374 toObject : function()
29376 var d = document.createElement('div');
29377 d.innerHTML = this.caption;
29379 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
29381 var iw = this.align == 'center' ? this.width : '100%';
29384 contenteditable : 'false',
29385 src : this.image_src,
29386 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29389 maxWidth : iw + ' !important', // this is not getting rendered?
29395 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29397 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
29402 if (this.href.length > 0) {
29406 contenteditable : 'true',
29414 if (this.video_url.length > 0) {
29419 allowfullscreen : true,
29420 width : 420, // these are for video tricks - that we replace the outer
29422 src : this.video_url,
29428 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29429 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29434 'data-block' : 'Figure',
29435 'data-width' : this.width,
29436 'data-caption' : this.caption,
29437 contenteditable : 'false',
29441 float : this.align ,
29442 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29443 width : this.align == 'center' ? '100%' : this.width,
29445 padding: this.align == 'center' ? '0' : '0 10px' ,
29446 textAlign : this.align // seems to work for email..
29451 align : this.align,
29457 'data-display' : this.caption_display,
29459 textAlign : 'left',
29461 lineHeight : '24px',
29462 display : this.caption_display,
29463 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
29465 width: this.align == 'center' ? this.width : '100%'
29469 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
29474 marginTop : '16px',
29480 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
29482 contenteditable : true,
29498 readElement : function(node)
29500 // this should not really come from the link...
29501 this.video_url = this.getVal(node, 'div', 'src');
29502 this.cls = this.getVal(node, 'div', 'class');
29503 this.href = this.getVal(node, 'a', 'href');
29506 this.image_src = this.getVal(node, 'img', 'src');
29508 this.align = this.getVal(node, 'figure', 'align');
29510 /// not really used - as hidden captions do not store the content here..
29511 var figcaption = this.getVal(node, 'figcaption', false);
29512 if (figcaption !== '') {
29513 this.caption = this.getVal(figcaption, 'i', 'html');
29517 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29518 var dc = this.getVal(node, true, 'data-caption');
29519 if (dc && dc.length) {
29522 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29523 this.width = this.getVal(node, true, 'data-width');
29524 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29527 removeNode : function()
29544 * @class Roo.htmleditor.BlockTable
29545 * Block that manages a table
29548 * Create a new Filter.
29549 * @param {Object} config Configuration options
29552 Roo.htmleditor.BlockTable = function(cfg)
29555 this.readElement(cfg.node);
29556 this.updateElement(cfg.node);
29558 Roo.apply(this, cfg);
29561 for(var r = 0; r < this.no_row; r++) {
29563 for(var c = 0; c < this.no_col; c++) {
29564 this.rows[r][c] = this.emptyCell();
29571 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29580 // used by context menu
29581 friendly_name : 'Table',
29582 deleteTitle : 'Delete Table',
29583 // context menu is drawn once..
29585 contextMenu : function(toolbar)
29588 var block = function() {
29589 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29593 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29595 var syncValue = toolbar.editorcore.syncValue;
29601 xtype : 'TextItem',
29603 xns : rooui.Toolbar //Boostrap?
29606 xtype : 'ComboBox',
29607 allowBlank : false,
29608 displayField : 'val',
29611 triggerAction : 'all',
29613 valueField : 'val',
29617 select : function (combo, r, index)
29619 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29621 b.width = r.get('val');
29624 toolbar.editorcore.onEditorEvent();
29629 xtype : 'SimpleStore',
29641 xtype : 'TextItem',
29642 text : "Columns: ",
29643 xns : rooui.Toolbar //Boostrap?
29650 click : function (_self, e)
29652 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29653 block().removeColumn();
29655 toolbar.editorcore.onEditorEvent();
29658 xns : rooui.Toolbar
29664 click : function (_self, e)
29666 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29667 block().addColumn();
29669 toolbar.editorcore.onEditorEvent();
29672 xns : rooui.Toolbar
29676 xtype : 'TextItem',
29678 xns : rooui.Toolbar //Boostrap?
29685 click : function (_self, e)
29687 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29688 block().removeRow();
29690 toolbar.editorcore.onEditorEvent();
29693 xns : rooui.Toolbar
29699 click : function (_self, e)
29703 toolbar.editorcore.onEditorEvent();
29706 xns : rooui.Toolbar
29711 text: 'Reset Column Widths',
29714 click : function (_self, e)
29716 block().resetWidths();
29718 toolbar.editorcore.onEditorEvent();
29721 xns : rooui.Toolbar
29732 * create a DomHelper friendly object - for use with
29733 * Roo.DomHelper.markup / overwrite / etc..
29734 * ?? should it be called with option to hide all editing features?
29736 toObject : function()
29741 contenteditable : 'false', // this stops cell selection from picking the table.
29742 'data-block' : 'Table',
29745 border : 'solid 1px #000', // ??? hard coded?
29746 'border-collapse' : 'collapse'
29749 { tag : 'tbody' , cn : [] }
29753 // do we have a head = not really
29755 Roo.each(this.rows, function( row ) {
29760 border : 'solid 1px #000',
29766 ret.cn[0].cn.push(tr);
29767 // does the row have any properties? ?? height?
29769 Roo.each(row, function( cell ) {
29773 contenteditable : 'true',
29774 'data-block' : 'Td',
29778 if (cell.colspan > 1) {
29779 td.colspan = cell.colspan ;
29780 nc += cell.colspan;
29784 if (cell.rowspan > 1) {
29785 td.rowspan = cell.rowspan ;
29794 ncols = Math.max(nc, ncols);
29798 // add the header row..
29807 readElement : function(node)
29809 node = node ? node : this.node ;
29810 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29814 var trs = Array.from(node.rows);
29815 trs.forEach(function(tr) {
29817 this.rows.push(row);
29821 Array.from(tr.cells).forEach(function(td) {
29824 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29825 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29826 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29827 html : td.innerHTML
29829 no_column += add.colspan;
29836 this.no_col = Math.max(this.no_col, no_column);
29843 normalizeRows: function()
29847 this.rows.forEach(function(row) {
29850 row = this.normalizeRow(row);
29852 row.forEach(function(c) {
29853 while (typeof(ret[rid][cid]) != 'undefined') {
29856 if (typeof(ret[rid]) == 'undefined') {
29862 if (c.rowspan < 2) {
29866 for(var i = 1 ;i < c.rowspan; i++) {
29867 if (typeof(ret[rid+i]) == 'undefined') {
29870 ret[rid+i][cid] = c;
29878 normalizeRow: function(row)
29881 row.forEach(function(c) {
29882 if (c.colspan < 2) {
29886 for(var i =0 ;i < c.colspan; i++) {
29894 deleteColumn : function(sel)
29896 if (!sel || sel.type != 'col') {
29899 if (this.no_col < 2) {
29903 this.rows.forEach(function(row) {
29904 var cols = this.normalizeRow(row);
29905 var col = cols[sel.col];
29906 if (col.colspan > 1) {
29916 removeColumn : function()
29918 this.deleteColumn({
29920 col : this.no_col-1
29922 this.updateElement();
29926 addColumn : function()
29929 this.rows.forEach(function(row) {
29930 row.push(this.emptyCell());
29933 this.updateElement();
29936 deleteRow : function(sel)
29938 if (!sel || sel.type != 'row') {
29942 if (this.no_row < 2) {
29946 var rows = this.normalizeRows();
29949 rows[sel.row].forEach(function(col) {
29950 if (col.rowspan > 1) {
29953 col.remove = 1; // flage it as removed.
29958 this.rows.forEach(function(row) {
29960 row.forEach(function(c) {
29961 if (typeof(c.remove) == 'undefined') {
29966 if (newrow.length > 0) {
29970 this.rows = newrows;
29975 this.updateElement();
29978 removeRow : function()
29982 row : this.no_row-1
29988 addRow : function()
29992 for (var i = 0; i < this.no_col; i++ ) {
29994 row.push(this.emptyCell());
29997 this.rows.push(row);
29998 this.updateElement();
30002 // the default cell object... at present...
30003 emptyCell : function() {
30004 return (new Roo.htmleditor.BlockTd({})).toObject();
30009 removeNode : function()
30016 resetWidths : function()
30018 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30019 var nn = Roo.htmleditor.Block.factory(n);
30021 nn.updateElement(n);
30034 * since selections really work on the table cell, then editing really should work from there
30036 * The original plan was to support merging etc... - but that may not be needed yet..
30038 * So this simple version will support:
30040 * adjust the width +/-
30041 * reset the width...
30050 * @class Roo.htmleditor.BlockTable
30051 * Block that manages a table
30054 * Create a new Filter.
30055 * @param {Object} config Configuration options
30058 Roo.htmleditor.BlockTd = function(cfg)
30061 this.readElement(cfg.node);
30062 this.updateElement(cfg.node);
30064 Roo.apply(this, cfg);
30069 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30074 textAlign : 'left',
30081 // used by context menu
30082 friendly_name : 'Table Cell',
30083 deleteTitle : false, // use our customer delete
30085 // context menu is drawn once..
30087 contextMenu : function(toolbar)
30090 var cell = function() {
30091 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30094 var table = function() {
30095 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30099 var saveSel = function()
30101 lr = toolbar.editorcore.getSelection().getRangeAt(0);
30103 var restoreSel = function()
30107 toolbar.editorcore.focus();
30108 var cr = toolbar.editorcore.getSelection();
30109 cr.removeAllRanges();
30111 toolbar.editorcore.onEditorEvent();
30112 }).defer(10, this);
30118 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30120 var syncValue = toolbar.editorcore.syncValue;
30127 text : 'Edit Table',
30129 click : function() {
30130 var t = toolbar.tb.selectedNode.closest('table');
30131 toolbar.editorcore.selectNode(t);
30132 toolbar.editorcore.onEditorEvent();
30141 xtype : 'TextItem',
30142 text : "Column Width: ",
30143 xns : rooui.Toolbar
30150 click : function (_self, e)
30152 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30153 cell().shrinkColumn();
30155 toolbar.editorcore.onEditorEvent();
30158 xns : rooui.Toolbar
30164 click : function (_self, e)
30166 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30167 cell().growColumn();
30169 toolbar.editorcore.onEditorEvent();
30172 xns : rooui.Toolbar
30176 xtype : 'TextItem',
30177 text : "Vertical Align: ",
30178 xns : rooui.Toolbar //Boostrap?
30181 xtype : 'ComboBox',
30182 allowBlank : false,
30183 displayField : 'val',
30186 triggerAction : 'all',
30188 valueField : 'val',
30192 select : function (combo, r, index)
30194 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30196 b.valign = r.get('val');
30199 toolbar.editorcore.onEditorEvent();
30204 xtype : 'SimpleStore',
30208 ['bottom'] // there are afew more...
30216 xtype : 'TextItem',
30217 text : "Merge Cells: ",
30218 xns : rooui.Toolbar
30227 click : function (_self, e)
30229 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30230 cell().mergeRight();
30231 //block().growColumn();
30233 toolbar.editorcore.onEditorEvent();
30236 xns : rooui.Toolbar
30243 click : function (_self, e)
30245 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30246 cell().mergeBelow();
30247 //block().growColumn();
30249 toolbar.editorcore.onEditorEvent();
30252 xns : rooui.Toolbar
30255 xtype : 'TextItem',
30257 xns : rooui.Toolbar
30265 click : function (_self, e)
30267 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30270 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30271 toolbar.editorcore.onEditorEvent();
30275 xns : rooui.Toolbar
30279 xns : rooui.Toolbar
30288 xns : rooui.Toolbar,
30297 click : function (_self, e)
30301 cell().deleteColumn();
30303 toolbar.editorcore.selectNode(t.node);
30304 toolbar.editorcore.onEditorEvent();
30313 click : function (_self, e)
30316 cell().deleteRow();
30319 toolbar.editorcore.selectNode(t.node);
30320 toolbar.editorcore.onEditorEvent();
30327 xtype : 'Separator',
30334 click : function (_self, e)
30337 var nn = t.node.nextSibling || t.node.previousSibling;
30338 t.node.parentNode.removeChild(t.node);
30340 toolbar.editorcore.selectNode(nn, true);
30342 toolbar.editorcore.onEditorEvent();
30352 // align... << fixme
30360 * create a DomHelper friendly object - for use with
30361 * Roo.DomHelper.markup / overwrite / etc..
30362 * ?? should it be called with option to hide all editing features?
30365 * create a DomHelper friendly object - for use with
30366 * Roo.DomHelper.markup / overwrite / etc..
30367 * ?? should it be called with option to hide all editing features?
30369 toObject : function()
30373 contenteditable : 'true', // this stops cell selection from picking the table.
30374 'data-block' : 'Td',
30375 valign : this.valign,
30377 'text-align' : this.textAlign,
30378 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30379 'border-collapse' : 'collapse',
30380 padding : '6px', // 8 for desktop / 4 for mobile
30381 'vertical-align': this.valign
30385 if (this.width != '') {
30386 ret.width = this.width;
30387 ret.style.width = this.width;
30391 if (this.colspan > 1) {
30392 ret.colspan = this.colspan ;
30394 if (this.rowspan > 1) {
30395 ret.rowspan = this.rowspan ;
30404 readElement : function(node)
30406 node = node ? node : this.node ;
30407 this.width = node.style.width;
30408 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30409 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30410 this.html = node.innerHTML;
30411 if (node.style.textAlign != '') {
30412 this.textAlign = node.style.textAlign;
30418 // the default cell object... at present...
30419 emptyCell : function() {
30423 textAlign : 'left',
30424 html : " " // is this going to be editable now?
30429 removeNode : function()
30431 return this.node.closest('table');
30439 toTableArray : function()
30442 var tab = this.node.closest('tr').closest('table');
30443 Array.from(tab.rows).forEach(function(r, ri){
30447 this.colWidths = [];
30448 var all_auto = true;
30449 Array.from(tab.rows).forEach(function(r, ri){
30452 Array.from(r.cells).forEach(function(ce, ci){
30457 colspan : ce.colSpan,
30458 rowspan : ce.rowSpan
30460 if (ce.isEqualNode(this.node)) {
30463 // if we have been filled up by a row?
30464 if (typeof(ret[rn][cn]) != 'undefined') {
30465 while(typeof(ret[rn][cn]) != 'undefined') {
30471 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30472 this.colWidths[cn] = ce.style.width;
30473 if (this.colWidths[cn] != '') {
30479 if (c.colspan < 2 && c.rowspan < 2 ) {
30484 for(var j = 0; j < c.rowspan; j++) {
30485 if (typeof(ret[rn+j]) == 'undefined') {
30486 continue; // we have a problem..
30489 for(var i = 0; i < c.colspan; i++) {
30490 ret[rn+j][cn+i] = c;
30499 // initalize widths.?
30500 // either all widths or no widths..
30502 this.colWidths[0] = false; // no widths flag.
30513 mergeRight: function()
30516 // get the contents of the next cell along..
30517 var tr = this.node.closest('tr');
30518 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30519 if (i >= tr.childNodes.length - 1) {
30520 return; // no cells on right to merge with.
30522 var table = this.toTableArray();
30524 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30525 return; // nothing right?
30527 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30528 // right cell - must be same rowspan and on the same row.
30529 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30530 return; // right hand side is not same rowspan.
30535 this.node.innerHTML += ' ' + rc.cell.innerHTML;
30536 tr.removeChild(rc.cell);
30537 this.colspan += rc.colspan;
30538 this.node.setAttribute('colspan', this.colspan);
30540 var table = this.toTableArray();
30541 this.normalizeWidths(table);
30542 this.updateWidths(table);
30546 mergeBelow : function()
30548 var table = this.toTableArray();
30549 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30550 return; // no row below
30552 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30553 return; // nothing right?
30555 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30557 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30558 return; // right hand side is not same rowspan.
30560 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30561 rc.cell.parentNode.removeChild(rc.cell);
30562 this.rowspan += rc.rowspan;
30563 this.node.setAttribute('rowspan', this.rowspan);
30568 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30571 var table = this.toTableArray();
30572 var cd = this.cellData;
30576 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30579 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30580 if (r == cd.row && c == cd.col) {
30581 this.node.removeAttribute('rowspan');
30582 this.node.removeAttribute('colspan');
30585 var ntd = this.node.cloneNode(); // which col/row should be 0..
30586 ntd.removeAttribute('id');
30587 ntd.style.width = this.colWidths[c];
30588 ntd.innerHTML = '';
30589 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30593 this.redrawAllCells(table);
30599 redrawAllCells: function(table)
30603 var tab = this.node.closest('tr').closest('table');
30604 var ctr = tab.rows[0].parentNode;
30605 Array.from(tab.rows).forEach(function(r, ri){
30607 Array.from(r.cells).forEach(function(ce, ci){
30608 ce.parentNode.removeChild(ce);
30610 r.parentNode.removeChild(r);
30612 for(var r = 0 ; r < table.length; r++) {
30613 var re = tab.rows[r];
30615 var re = tab.ownerDocument.createElement('tr');
30616 ctr.appendChild(re);
30617 for(var c = 0 ; c < table[r].length; c++) {
30618 if (table[r][c].cell === false) {
30622 re.appendChild(table[r][c].cell);
30624 table[r][c].cell = false;
30629 updateWidths : function(table)
30631 for(var r = 0 ; r < table.length; r++) {
30633 for(var c = 0 ; c < table[r].length; c++) {
30634 if (table[r][c].cell === false) {
30638 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30639 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30640 el.width = Math.floor(this.colWidths[c]) +'%';
30641 el.updateElement(el.node);
30643 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30644 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30646 for(var i = 0; i < table[r][c].colspan; i ++) {
30647 width += Math.floor(this.colWidths[c + i]);
30649 el.width = width +'%';
30650 el.updateElement(el.node);
30652 table[r][c].cell = false; // done
30656 normalizeWidths : function(table)
30658 if (this.colWidths[0] === false) {
30659 var nw = 100.0 / this.colWidths.length;
30660 this.colWidths.forEach(function(w,i) {
30661 this.colWidths[i] = nw;
30666 var t = 0, missing = [];
30668 this.colWidths.forEach(function(w,i) {
30670 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30671 var add = this.colWidths[i];
30680 var nc = this.colWidths.length;
30681 if (missing.length) {
30682 var mult = (nc - missing.length) / (1.0 * nc);
30684 var ew = (100 -t) / (1.0 * missing.length);
30685 this.colWidths.forEach(function(w,i) {
30687 this.colWidths[i] = w * mult;
30691 this.colWidths[i] = ew;
30693 // have to make up numbers..
30696 // now we should have all the widths..
30701 shrinkColumn : function()
30703 var table = this.toTableArray();
30704 this.normalizeWidths(table);
30705 var col = this.cellData.col;
30706 var nw = this.colWidths[col] * 0.8;
30710 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30711 this.colWidths.forEach(function(w,i) {
30713 this.colWidths[i] = nw;
30716 this.colWidths[i] += otherAdd
30718 this.updateWidths(table);
30721 growColumn : function()
30723 var table = this.toTableArray();
30724 this.normalizeWidths(table);
30725 var col = this.cellData.col;
30726 var nw = this.colWidths[col] * 1.2;
30730 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30731 this.colWidths.forEach(function(w,i) {
30733 this.colWidths[i] = nw;
30736 this.colWidths[i] -= otherSub
30738 this.updateWidths(table);
30741 deleteRow : function()
30743 // delete this rows 'tr'
30744 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30745 // then reduce the rowspan.
30746 var table = this.toTableArray();
30747 // this.cellData.row;
30748 for (var i =0;i< table[this.cellData.row].length ; i++) {
30749 var c = table[this.cellData.row][i];
30750 if (c.row != this.cellData.row) {
30753 c.cell.setAttribute('rowspan', c.rowspan);
30756 if (c.rowspan > 1) {
30758 c.cell.setAttribute('rowspan', c.rowspan);
30761 table.splice(this.cellData.row,1);
30762 this.redrawAllCells(table);
30765 deleteColumn : function()
30767 var table = this.toTableArray();
30769 for (var i =0;i< table.length ; i++) {
30770 var c = table[i][this.cellData.col];
30771 if (c.col != this.cellData.col) {
30772 table[i][this.cellData.col].colspan--;
30773 } else if (c.colspan > 1) {
30775 c.cell.setAttribute('colspan', c.colspan);
30777 table[i].splice(this.cellData.col,1);
30780 this.redrawAllCells(table);
30788 //<script type="text/javascript">
30791 * Based Ext JS Library 1.1.1
30792 * Copyright(c) 2006-2007, Ext JS, LLC.
30798 * @class Roo.HtmlEditorCore
30799 * @extends Roo.Component
30800 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30802 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30805 Roo.HtmlEditorCore = function(config){
30808 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30813 * @event initialize
30814 * Fires when the editor is fully initialized (including the iframe)
30815 * @param {Roo.HtmlEditorCore} this
30820 * Fires when the editor is first receives the focus. Any insertion must wait
30821 * until after this event.
30822 * @param {Roo.HtmlEditorCore} this
30826 * @event beforesync
30827 * Fires before the textarea is updated with content from the editor iframe. Return false
30828 * to cancel the sync.
30829 * @param {Roo.HtmlEditorCore} this
30830 * @param {String} html
30834 * @event beforepush
30835 * Fires before the iframe editor is updated with content from the textarea. Return false
30836 * to cancel the push.
30837 * @param {Roo.HtmlEditorCore} this
30838 * @param {String} html
30843 * Fires when the textarea is updated with content from the editor iframe.
30844 * @param {Roo.HtmlEditorCore} this
30845 * @param {String} html
30850 * Fires when the iframe editor is updated with content from the textarea.
30851 * @param {Roo.HtmlEditorCore} this
30852 * @param {String} html
30857 * @event editorevent
30858 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30859 * @param {Roo.HtmlEditorCore} this
30866 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30868 // defaults : white / black...
30869 this.applyBlacklists();
30876 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30880 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30886 * @cfg {String} css styling for resizing. (used on bootstrap only)
30890 * @cfg {Number} height (in pixels)
30894 * @cfg {Number} width (in pixels)
30898 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30899 * if you are doing an email editor, this probably needs disabling, it's designed
30904 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30906 enableBlocks : true,
30908 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30911 stylesheets: false,
30913 * @cfg {String} language default en - language of text (usefull for rtl languages)
30919 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30920 * - by default they are stripped - if you are editing email you may need this.
30922 allowComments: false,
30926 // private properties
30927 validationEvent : false,
30929 initialized : false,
30931 sourceEditMode : false,
30932 onFocus : Roo.emptyFn,
30934 hideMode:'offsets',
30938 // blacklist + whitelisted elements..
30945 undoManager : false,
30947 * Protected method that will not generally be called directly. It
30948 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30949 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30951 getDocMarkup : function(){
30955 // inherit styels from page...??
30956 if (this.stylesheets === false) {
30958 Roo.get(document.head).select('style').each(function(node) {
30959 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30962 Roo.get(document.head).select('link').each(function(node) {
30963 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30966 } else if (!this.stylesheets.length) {
30968 st = '<style type="text/css">' +
30969 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30972 for (var i in this.stylesheets) {
30973 if (typeof(this.stylesheets[i]) != 'string') {
30976 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30981 st += '<style type="text/css">' +
30982 'IMG { cursor: pointer } ' +
30985 st += '<meta name="google" content="notranslate">';
30987 var cls = 'notranslate roo-htmleditor-body';
30989 if(this.bodyCls.length){
30990 cls += ' ' + this.bodyCls;
30993 return '<html class="notranslate" translate="no"><head>' + st +
30994 //<style type="text/css">' +
30995 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30997 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
31001 onRender : function(ct, position)
31004 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31005 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31008 this.el.dom.style.border = '0 none';
31009 this.el.dom.setAttribute('tabIndex', -1);
31010 this.el.addClass('x-hidden hide');
31014 if(Roo.isIE){ // fix IE 1px bogus margin
31015 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31019 this.frameId = Roo.id();
31023 cls: 'form-control', // bootstrap..
31025 name: this.frameId,
31026 frameBorder : 'no',
31027 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
31030 ifcfg.style = { resize : this.resize };
31033 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
31036 this.iframe = iframe.dom;
31038 this.assignDocWin();
31040 this.doc.designMode = 'on';
31043 this.doc.write(this.getDocMarkup());
31047 var task = { // must defer to wait for browser to be ready
31049 //console.log("run task?" + this.doc.readyState);
31050 this.assignDocWin();
31051 if(this.doc.body || this.doc.readyState == 'complete'){
31053 this.doc.designMode="on";
31058 Roo.TaskMgr.stop(task);
31059 this.initEditor.defer(10, this);
31066 Roo.TaskMgr.start(task);
31071 onResize : function(w, h)
31073 Roo.log('resize: ' +w + ',' + h );
31074 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31078 if(typeof w == 'number'){
31080 this.iframe.style.width = w + 'px';
31082 if(typeof h == 'number'){
31084 this.iframe.style.height = h + 'px';
31086 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31093 * Toggles the editor between standard and source edit mode.
31094 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31096 toggleSourceEdit : function(sourceEditMode){
31098 this.sourceEditMode = sourceEditMode === true;
31100 if(this.sourceEditMode){
31102 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
31105 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31106 //this.iframe.className = '';
31109 //this.setSize(this.owner.wrap.getSize());
31110 //this.fireEvent('editmodechange', this, this.sourceEditMode);
31117 * Protected method that will not generally be called directly. If you need/want
31118 * custom HTML cleanup, this is the method you should override.
31119 * @param {String} html The HTML to be cleaned
31120 * return {String} The cleaned HTML
31122 cleanHtml : function(html)
31124 html = String(html);
31125 if(html.length > 5){
31126 if(Roo.isSafari){ // strip safari nonsense
31127 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31130 if(html == ' '){
31137 * HTML Editor -> Textarea
31138 * Protected method that will not generally be called directly. Syncs the contents
31139 * of the editor iframe with the textarea.
31141 syncValue : function()
31143 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31144 if(this.initialized){
31146 if (this.undoManager) {
31147 this.undoManager.addEvent();
31151 var bd = (this.doc.body || this.doc.documentElement);
31154 var sel = this.win.getSelection();
31156 var div = document.createElement('div');
31157 div.innerHTML = bd.innerHTML;
31158 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31159 if (gtx.length > 0) {
31160 var rm = gtx.item(0).parentNode;
31161 rm.parentNode.removeChild(rm);
31165 if (this.enableBlocks) {
31166 new Roo.htmleditor.FilterBlock({ node : div });
31169 var html = div.innerHTML;
31172 if (this.autoClean) {
31174 new Roo.htmleditor.FilterAttributes({
31196 attrib_clean : ['href', 'src' ]
31199 var tidy = new Roo.htmleditor.TidySerializer({
31202 html = tidy.serialize(div);
31208 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31209 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31211 html = '<div style="'+m[0]+'">' + html + '</div>';
31214 html = this.cleanHtml(html);
31215 // fix up the special chars.. normaly like back quotes in word...
31216 // however we do not want to do this with chinese..
31217 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31219 var cc = match.charCodeAt();
31221 // Get the character value, handling surrogate pairs
31222 if (match.length == 2) {
31223 // It's a surrogate pair, calculate the Unicode code point
31224 var high = match.charCodeAt(0) - 0xD800;
31225 var low = match.charCodeAt(1) - 0xDC00;
31226 cc = (high * 0x400) + low + 0x10000;
31228 (cc >= 0x4E00 && cc < 0xA000 ) ||
31229 (cc >= 0x3400 && cc < 0x4E00 ) ||
31230 (cc >= 0xf900 && cc < 0xfb00 )
31235 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31236 return "&#" + cc + ";";
31243 if(this.owner.fireEvent('beforesync', this, html) !== false){
31244 this.el.dom.value = html;
31245 this.owner.fireEvent('sync', this, html);
31251 * TEXTAREA -> EDITABLE
31252 * Protected method that will not generally be called directly. Pushes the value of the textarea
31253 * into the iframe editor.
31255 pushValue : function()
31257 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31258 if(this.initialized){
31259 var v = this.el.dom.value.trim();
31262 if(this.owner.fireEvent('beforepush', this, v) !== false){
31263 var d = (this.doc.body || this.doc.documentElement);
31266 this.el.dom.value = d.innerHTML;
31267 this.owner.fireEvent('push', this, v);
31269 if (this.autoClean) {
31270 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31271 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31273 if (this.enableBlocks) {
31274 Roo.htmleditor.Block.initAll(this.doc.body);
31277 this.updateLanguage();
31279 var lc = this.doc.body.lastChild;
31280 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31281 // add an extra line at the end.
31282 this.doc.body.appendChild(this.doc.createElement('br'));
31290 deferFocus : function(){
31291 this.focus.defer(10, this);
31295 focus : function(){
31296 if(this.win && !this.sourceEditMode){
31303 assignDocWin: function()
31305 var iframe = this.iframe;
31308 this.doc = iframe.contentWindow.document;
31309 this.win = iframe.contentWindow;
31311 // if (!Roo.get(this.frameId)) {
31314 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31315 // this.win = Roo.get(this.frameId).dom.contentWindow;
31317 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31321 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31322 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31327 initEditor : function(){
31328 //console.log("INIT EDITOR");
31329 this.assignDocWin();
31333 this.doc.designMode="on";
31335 this.doc.write(this.getDocMarkup());
31338 var dbody = (this.doc.body || this.doc.documentElement);
31339 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31340 // this copies styles from the containing element into thsi one..
31341 // not sure why we need all of this..
31342 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31344 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31345 //ss['background-attachment'] = 'fixed'; // w3c
31346 dbody.bgProperties = 'fixed'; // ie
31347 dbody.setAttribute("translate", "no");
31349 //Roo.DomHelper.applyStyles(dbody, ss);
31350 Roo.EventManager.on(this.doc, {
31352 'mouseup': this.onEditorEvent,
31353 'dblclick': this.onEditorEvent,
31354 'click': this.onEditorEvent,
31355 'keyup': this.onEditorEvent,
31360 Roo.EventManager.on(this.doc, {
31361 'paste': this.onPasteEvent,
31365 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31368 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31369 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31371 this.initialized = true;
31374 // initialize special key events - enter
31375 new Roo.htmleditor.KeyEnter({core : this});
31379 this.owner.fireEvent('initialize', this);
31382 // this is to prevent a href clicks resulting in a redirect?
31384 onPasteEvent : function(e,v)
31386 // I think we better assume paste is going to be a dirty load of rubish from word..
31388 // even pasting into a 'email version' of this widget will have to clean up that mess.
31389 var cd = (e.browserEvent.clipboardData || window.clipboardData);
31391 // check what type of paste - if it's an image, then handle it differently.
31392 if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31394 var urlAPI = (window.createObjectURL && window) ||
31395 (window.URL && URL.revokeObjectURL && URL) ||
31396 (window.webkitURL && webkitURL);
31398 var r = new FileReader();
31400 r.addEventListener('load',function()
31403 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31404 // is insert asycn?
31405 if (t.enableBlocks) {
31407 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31408 if (img.closest('figure')) { // assume!! that it's aready
31411 var fig = new Roo.htmleditor.BlockFigure({
31412 image_src : img.src
31414 fig.updateElement(img); // replace it..
31418 t.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31419 t.owner.fireEvent('paste', this);
31421 r.readAsDataURL(cd.files[0]);
31423 e.preventDefault();
31427 if (cd.types.indexOf('text/html') < 0 ) {
31431 var html = cd.getData('text/html'); // clipboard event
31432 if (cd.types.indexOf('text/rtf') > -1) {
31433 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31434 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31439 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31440 .map(function(g) { return g.toDataURL(); })
31441 .filter(function(g) { return g != 'about:blank'; });
31444 html = this.cleanWordChars(html);
31446 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31449 var sn = this.getParentElement();
31450 // check if d contains a table, and prevent nesting??
31451 //Roo.log(d.getElementsByTagName('table'));
31453 //Roo.log(sn.closest('table'));
31454 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31455 e.preventDefault();
31456 this.insertAtCursor("You can not nest tables");
31457 //Roo.log("prevent?"); // fixme -
31463 if (images.length > 0) {
31464 // replace all v:imagedata - with img.
31465 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31466 Roo.each(ar, function(node) {
31467 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31468 node.parentNode.removeChild(node);
31472 Roo.each(d.getElementsByTagName('img'), function(img, i) {
31473 img.setAttribute('src', images[i]);
31476 if (this.autoClean) {
31477 new Roo.htmleditor.FilterWord({ node : d });
31479 new Roo.htmleditor.FilterStyleToTag({ node : d });
31480 new Roo.htmleditor.FilterAttributes({
31482 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31483 attrib_clean : ['href', 'src' ]
31485 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31486 // should be fonts..
31487 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31488 new Roo.htmleditor.FilterParagraph({ node : d });
31489 new Roo.htmleditor.FilterSpan({ node : d });
31490 new Roo.htmleditor.FilterLongBr({ node : d });
31491 new Roo.htmleditor.FilterComment({ node : d });
31495 if (this.enableBlocks) {
31497 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31498 if (img.closest('figure')) { // assume!! that it's aready
31501 var fig = new Roo.htmleditor.BlockFigure({
31502 image_src : img.src
31504 fig.updateElement(img); // replace it..
31510 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31511 if (this.enableBlocks) {
31512 Roo.htmleditor.Block.initAll(this.doc.body);
31516 e.preventDefault();
31517 this.owner.fireEvent('paste', this);
31519 // default behaveiour should be our local cleanup paste? (optional?)
31520 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31521 //this.owner.fireEvent('paste', e, v);
31524 onDestroy : function(){
31530 //for (var i =0; i < this.toolbars.length;i++) {
31531 // // fixme - ask toolbars for heights?
31532 // this.toolbars[i].onDestroy();
31535 //this.wrap.dom.innerHTML = '';
31536 //this.wrap.remove();
31541 onFirstFocus : function(){
31543 this.assignDocWin();
31544 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31546 this.activated = true;
31549 if(Roo.isGecko){ // prevent silly gecko errors
31551 var s = this.win.getSelection();
31552 if(!s.focusNode || s.focusNode.nodeType != 3){
31553 var r = s.getRangeAt(0);
31554 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31559 this.execCmd('useCSS', true);
31560 this.execCmd('styleWithCSS', false);
31563 this.owner.fireEvent('activate', this);
31567 adjustFont: function(btn){
31568 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31569 //if(Roo.isSafari){ // safari
31572 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31573 if(Roo.isSafari){ // safari
31574 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31575 v = (v < 10) ? 10 : v;
31576 v = (v > 48) ? 48 : v;
31577 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31582 v = Math.max(1, v+adjust);
31584 this.execCmd('FontSize', v );
31587 onEditorEvent : function(e)
31591 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31592 return; // we do not handle this.. (undo manager does..)
31594 // clicking a 'block'?
31596 // in theory this detects if the last element is not a br, then we try and do that.
31597 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31599 e.target.nodeName == 'BODY' &&
31600 e.type == "mouseup" &&
31601 this.doc.body.lastChild
31603 var lc = this.doc.body.lastChild;
31604 // gtx-trans is google translate plugin adding crap.
31605 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31606 lc = lc.previousSibling;
31608 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31609 // if last element is <BR> - then dont do anything.
31611 var ns = this.doc.createElement('br');
31612 this.doc.body.appendChild(ns);
31613 range = this.doc.createRange();
31614 range.setStartAfter(ns);
31615 range.collapse(true);
31616 var sel = this.win.getSelection();
31617 sel.removeAllRanges();
31618 sel.addRange(range);
31624 this.fireEditorEvent(e);
31625 // this.updateToolbar();
31626 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31629 fireEditorEvent: function(e)
31631 this.owner.fireEvent('editorevent', this, e);
31634 insertTag : function(tg)
31636 // could be a bit smarter... -> wrap the current selected tRoo..
31637 if (tg.toLowerCase() == 'span' ||
31638 tg.toLowerCase() == 'code' ||
31639 tg.toLowerCase() == 'sup' ||
31640 tg.toLowerCase() == 'sub'
31643 range = this.createRange(this.getSelection());
31644 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31645 wrappingNode.appendChild(range.extractContents());
31646 range.insertNode(wrappingNode);
31653 this.execCmd("formatblock", tg);
31654 this.undoManager.addEvent();
31657 insertText : function(txt)
31661 var range = this.createRange();
31662 range.deleteContents();
31663 //alert(Sender.getAttribute('label'));
31665 range.insertNode(this.doc.createTextNode(txt));
31666 this.undoManager.addEvent();
31672 * Executes a Midas editor command on the editor document and performs necessary focus and
31673 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31674 * @param {String} cmd The Midas command
31675 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31677 relayCmd : function(cmd, value)
31681 case 'justifyleft':
31682 case 'justifyright':
31683 case 'justifycenter':
31684 // if we are in a cell, then we will adjust the
31685 var n = this.getParentElement();
31686 var td = n.closest('td');
31688 var bl = Roo.htmleditor.Block.factory(td);
31689 bl.textAlign = cmd.replace('justify','');
31690 bl.updateElement();
31691 this.owner.fireEvent('editorevent', this);
31694 this.execCmd('styleWithCSS', true); //
31699 // if there is no selection, then we insert, and set the curson inside it..
31700 this.execCmd('styleWithCSS', false);
31710 this.execCmd(cmd, value);
31711 this.owner.fireEvent('editorevent', this);
31712 //this.updateToolbar();
31713 this.owner.deferFocus();
31717 * Executes a Midas editor command directly on the editor document.
31718 * For visual commands, you should use {@link #relayCmd} instead.
31719 * <b>This should only be called after the editor is initialized.</b>
31720 * @param {String} cmd The Midas command
31721 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31723 execCmd : function(cmd, value){
31724 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31731 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31733 * @param {String} text | dom node..
31735 insertAtCursor : function(text)
31738 if(!this.activated){
31742 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31746 // from jquery ui (MIT licenced)
31748 var win = this.win;
31750 if (win.getSelection && win.getSelection().getRangeAt) {
31752 // delete the existing?
31754 this.createRange(this.getSelection()).deleteContents();
31755 range = win.getSelection().getRangeAt(0);
31756 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31757 range.insertNode(node);
31758 range = range.cloneRange();
31759 range.collapse(false);
31761 win.getSelection().removeAllRanges();
31762 win.getSelection().addRange(range);
31766 } else if (win.document.selection && win.document.selection.createRange) {
31767 // no firefox support
31768 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31769 win.document.selection.createRange().pasteHTML(txt);
31772 // no firefox support
31773 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31774 this.execCmd('InsertHTML', txt);
31782 mozKeyPress : function(e){
31784 var c = e.getCharCode(), cmd;
31787 c = String.fromCharCode(c).toLowerCase();
31801 // this.cleanUpPaste.defer(100, this);
31807 this.relayCmd(cmd);
31808 //this.win.focus();
31809 //this.execCmd(cmd);
31810 //this.deferFocus();
31811 e.preventDefault();
31819 fixKeys : function(){ // load time branching for fastest keydown performance
31823 return function(e){
31824 var k = e.getKey(), r;
31827 r = this.doc.selection.createRange();
31830 r.pasteHTML('    ');
31835 /// this is handled by Roo.htmleditor.KeyEnter
31838 r = this.doc.selection.createRange();
31840 var target = r.parentElement();
31841 if(!target || target.tagName.toLowerCase() != 'li'){
31843 r.pasteHTML('<br/>');
31850 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31851 // this.cleanUpPaste.defer(100, this);
31857 }else if(Roo.isOpera){
31858 return function(e){
31859 var k = e.getKey();
31863 this.execCmd('InsertHTML','    ');
31867 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31868 // this.cleanUpPaste.defer(100, this);
31873 }else if(Roo.isSafari){
31874 return function(e){
31875 var k = e.getKey();
31879 this.execCmd('InsertText','\t');
31883 this.mozKeyPress(e);
31885 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31886 // this.cleanUpPaste.defer(100, this);
31894 getAllAncestors: function()
31896 var p = this.getSelectedNode();
31899 a.push(p); // push blank onto stack..
31900 p = this.getParentElement();
31904 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31908 a.push(this.doc.body);
31912 lastSelNode : false,
31915 getSelection : function()
31917 this.assignDocWin();
31918 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31921 * Select a dom node
31922 * @param {DomElement} node the node to select
31924 selectNode : function(node, collapse)
31926 var nodeRange = node.ownerDocument.createRange();
31928 nodeRange.selectNode(node);
31930 nodeRange.selectNodeContents(node);
31932 if (collapse === true) {
31933 nodeRange.collapse(true);
31936 var s = this.win.getSelection();
31937 s.removeAllRanges();
31938 s.addRange(nodeRange);
31941 getSelectedNode: function()
31943 // this may only work on Gecko!!!
31945 // should we cache this!!!!
31949 var range = this.createRange(this.getSelection()).cloneRange();
31952 var parent = range.parentElement();
31954 var testRange = range.duplicate();
31955 testRange.moveToElementText(parent);
31956 if (testRange.inRange(range)) {
31959 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31962 parent = parent.parentElement;
31967 // is ancestor a text element.
31968 var ac = range.commonAncestorContainer;
31969 if (ac.nodeType == 3) {
31970 ac = ac.parentNode;
31973 var ar = ac.childNodes;
31976 var other_nodes = [];
31977 var has_other_nodes = false;
31978 for (var i=0;i<ar.length;i++) {
31979 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31982 // fullly contained node.
31984 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31989 // probably selected..
31990 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31991 other_nodes.push(ar[i]);
31995 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
32000 has_other_nodes = true;
32002 if (!nodes.length && other_nodes.length) {
32003 nodes= other_nodes;
32005 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32013 createRange: function(sel)
32015 // this has strange effects when using with
32016 // top toolbar - not sure if it's a great idea.
32017 //this.editor.contentWindow.focus();
32018 if (typeof sel != "undefined") {
32020 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32022 return this.doc.createRange();
32025 return this.doc.createRange();
32028 getParentElement: function()
32031 this.assignDocWin();
32032 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32034 var range = this.createRange(sel);
32037 var p = range.commonAncestorContainer;
32038 while (p.nodeType == 3) { // text node
32049 * Range intersection.. the hard stuff...
32053 * [ -- selected range --- ]
32057 * if end is before start or hits it. fail.
32058 * if start is after end or hits it fail.
32060 * if either hits (but other is outside. - then it's not
32066 // @see http://www.thismuchiknow.co.uk/?p=64.
32067 rangeIntersectsNode : function(range, node)
32069 var nodeRange = node.ownerDocument.createRange();
32071 nodeRange.selectNode(node);
32073 nodeRange.selectNodeContents(node);
32076 var rangeStartRange = range.cloneRange();
32077 rangeStartRange.collapse(true);
32079 var rangeEndRange = range.cloneRange();
32080 rangeEndRange.collapse(false);
32082 var nodeStartRange = nodeRange.cloneRange();
32083 nodeStartRange.collapse(true);
32085 var nodeEndRange = nodeRange.cloneRange();
32086 nodeEndRange.collapse(false);
32088 return rangeStartRange.compareBoundaryPoints(
32089 Range.START_TO_START, nodeEndRange) == -1 &&
32090 rangeEndRange.compareBoundaryPoints(
32091 Range.START_TO_START, nodeStartRange) == 1;
32095 rangeCompareNode : function(range, node)
32097 var nodeRange = node.ownerDocument.createRange();
32099 nodeRange.selectNode(node);
32101 nodeRange.selectNodeContents(node);
32105 range.collapse(true);
32107 nodeRange.collapse(true);
32109 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32110 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
32112 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32114 var nodeIsBefore = ss == 1;
32115 var nodeIsAfter = ee == -1;
32117 if (nodeIsBefore && nodeIsAfter) {
32120 if (!nodeIsBefore && nodeIsAfter) {
32121 return 1; //right trailed.
32124 if (nodeIsBefore && !nodeIsAfter) {
32125 return 2; // left trailed.
32131 cleanWordChars : function(input) {// change the chars to hex code
32134 [ 8211, "–" ],
32135 [ 8212, "—" ],
32143 var output = input;
32144 Roo.each(swapCodes, function(sw) {
32145 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32147 output = output.replace(swapper, sw[1]);
32157 cleanUpChild : function (node)
32160 new Roo.htmleditor.FilterComment({node : node});
32161 new Roo.htmleditor.FilterAttributes({
32163 attrib_black : this.ablack,
32164 attrib_clean : this.aclean,
32165 style_white : this.cwhite,
32166 style_black : this.cblack
32168 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32169 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32175 * Clean up MS wordisms...
32176 * @deprecated - use filter directly
32178 cleanWord : function(node)
32180 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32181 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32188 * @deprecated - use filters
32190 cleanTableWidths : function(node)
32192 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32199 applyBlacklists : function()
32201 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
32202 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
32204 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
32205 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
32206 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
32210 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32211 if (b.indexOf(tag) > -1) {
32214 this.white.push(tag);
32218 Roo.each(w, function(tag) {
32219 if (b.indexOf(tag) > -1) {
32222 if (this.white.indexOf(tag) > -1) {
32225 this.white.push(tag);
32230 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32231 if (w.indexOf(tag) > -1) {
32234 this.black.push(tag);
32238 Roo.each(b, function(tag) {
32239 if (w.indexOf(tag) > -1) {
32242 if (this.black.indexOf(tag) > -1) {
32245 this.black.push(tag);
32250 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
32251 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
32255 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32256 if (b.indexOf(tag) > -1) {
32259 this.cwhite.push(tag);
32263 Roo.each(w, function(tag) {
32264 if (b.indexOf(tag) > -1) {
32267 if (this.cwhite.indexOf(tag) > -1) {
32270 this.cwhite.push(tag);
32275 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32276 if (w.indexOf(tag) > -1) {
32279 this.cblack.push(tag);
32283 Roo.each(b, function(tag) {
32284 if (w.indexOf(tag) > -1) {
32287 if (this.cblack.indexOf(tag) > -1) {
32290 this.cblack.push(tag);
32295 setStylesheets : function(stylesheets)
32297 if(typeof(stylesheets) == 'string'){
32298 Roo.get(this.iframe.contentDocument.head).createChild({
32300 rel : 'stylesheet',
32309 Roo.each(stylesheets, function(s) {
32314 Roo.get(_this.iframe.contentDocument.head).createChild({
32316 rel : 'stylesheet',
32326 updateLanguage : function()
32328 if (!this.iframe || !this.iframe.contentDocument) {
32331 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32335 removeStylesheets : function()
32339 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32344 setStyle : function(style)
32346 Roo.get(this.iframe.contentDocument.head).createChild({
32355 // hide stuff that is not compatible
32369 * @event specialkey
32373 * @cfg {String} fieldClass @hide
32376 * @cfg {String} focusClass @hide
32379 * @cfg {String} autoCreate @hide
32382 * @cfg {String} inputType @hide
32385 * @cfg {String} invalidClass @hide
32388 * @cfg {String} invalidText @hide
32391 * @cfg {String} msgFx @hide
32394 * @cfg {String} validateOnBlur @hide
32398 Roo.HtmlEditorCore.white = [
32399 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32401 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
32402 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
32403 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
32404 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
32405 'TABLE', 'UL', 'XMP',
32407 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
32410 'DIR', 'MENU', 'OL', 'UL', 'DL',
32416 Roo.HtmlEditorCore.black = [
32417 // 'embed', 'object', // enable - backend responsiblity to clean thiese
32419 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
32420 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
32421 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
32422 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
32423 //'FONT' // CLEAN LATER..
32424 'COLGROUP', 'COL' // messy tables.
32428 Roo.HtmlEditorCore.clean = [ // ?? needed???
32429 'SCRIPT', 'STYLE', 'TITLE', 'XML'
32431 Roo.HtmlEditorCore.tag_remove = [
32436 Roo.HtmlEditorCore.ablack = [
32440 Roo.HtmlEditorCore.aclean = [
32441 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
32445 Roo.HtmlEditorCore.pwhite= [
32446 'http', 'https', 'mailto'
32449 // white listed style attributes.
32450 Roo.HtmlEditorCore.cwhite= [
32451 // 'text-align', /// default is to allow most things..
32457 // black listed style attributes.
32458 Roo.HtmlEditorCore.cblack= [
32459 // 'font-size' -- this can be set by the project
32473 * @class Roo.bootstrap.form.HtmlEditor
32474 * @extends Roo.bootstrap.form.TextArea
32475 * Bootstrap HtmlEditor class
32478 * Create a new HtmlEditor
32479 * @param {Object} config The config object
32482 Roo.bootstrap.form.HtmlEditor = function(config){
32486 * @event initialize
32487 * Fires when the editor is fully initialized (including the iframe)
32488 * @param {Roo.bootstrap.form.HtmlEditor} this
32493 * Fires when the editor is first receives the focus. Any insertion must wait
32494 * until after this event.
32495 * @param {Roo.bootstrap.form.HtmlEditor} this
32499 * @event beforesync
32500 * Fires before the textarea is updated with content from the editor iframe. Return false
32501 * to cancel the sync.
32502 * @param {Roo.bootstrap.form.HtmlEditor} this
32503 * @param {String} html
32507 * @event beforepush
32508 * Fires before the iframe editor is updated with content from the textarea. Return false
32509 * to cancel the push.
32510 * @param {Roo.bootstrap.form.HtmlEditor} this
32511 * @param {String} html
32516 * Fires when the textarea is updated with content from the editor iframe.
32517 * @param {Roo.bootstrap.form.HtmlEditor} this
32518 * @param {String} html
32523 * Fires when the iframe editor is updated with content from the textarea.
32524 * @param {Roo.bootstrap.form.HtmlEditor} this
32525 * @param {String} html
32529 * @event editmodechange
32530 * Fires when the editor switches edit modes
32531 * @param {Roo.bootstrap.form.HtmlEditor} this
32532 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32534 editmodechange: true,
32536 * @event editorevent
32537 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32538 * @param {Roo.bootstrap.form.HtmlEditor} this
32542 * @event firstfocus
32543 * Fires when on first focus - needed by toolbars..
32544 * @param {Roo.bootstrap.form.HtmlEditor} this
32549 * Auto save the htmlEditor value as a file into Events
32550 * @param {Roo.bootstrap.form.HtmlEditor} this
32554 * @event savedpreview
32555 * preview the saved version of htmlEditor
32556 * @param {Roo.bootstrap.form.HtmlEditor} this
32558 savedpreview: true,
32560 * @event stylesheetsclick
32561 * Fires when press the Sytlesheets button
32562 * @param {Roo.HtmlEditorCore} this
32564 stylesheetsclick: true,
32567 * Fires when press user pastes into the editor
32568 * @param {Roo.HtmlEditorCore} this
32573 * Fires when on any editor when an image is added (excluding paste)
32574 * @param {Roo.bootstrap.form.HtmlEditor} this
32578 * @event imageupdated
32579 * Fires when on any editor when an image is changed (excluding paste)
32580 * @param {Roo.bootstrap.form.HtmlEditor} this
32581 * @param {HTMLElement} img could also be a figure if blocks are enabled
32583 imageupdate: true ,
32585 * @event imagedelete
32586 * Fires when on any editor when an image is deleted
32587 * @param {Roo.bootstrap.form.HtmlEditor} this
32588 * @param {HTMLElement} img could also be a figure if blocks are enabled
32592 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32593 if (!this.toolbars) {
32594 this.toolbars = [];
32597 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32602 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
32606 * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32611 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32616 * @cfg {String} resize (none|both|horizontal|vertical) - css resize of element
32620 * @cfg {Number} height (in pixels)
32624 * @cfg {Number} width (in pixels)
32629 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32632 stylesheets: false,
32637 // private properties
32638 validationEvent : false,
32640 initialized : false,
32643 onFocus : Roo.emptyFn,
32645 hideMode:'offsets',
32647 tbContainer : false,
32651 toolbarContainer :function() {
32652 return this.wrap.select('.x-html-editor-tb',true).first();
32656 * Protected method that will not generally be called directly. It
32657 * is called when the editor creates its toolbar. Override this method if you need to
32658 * add custom toolbar buttons.
32659 * @param {HtmlEditor} editor
32661 createToolbar : function()
32663 //Roo.log('renewing');
32664 //Roo.log("create toolbars");
32665 if (this.toolbars === false) {
32668 if (this.toolbars === true) {
32669 this.toolbars = [ 'Standard' ];
32672 var ar = Array.from(this.toolbars);
32673 this.toolbars = [];
32674 ar.forEach(function(t,i) {
32675 if (typeof(t) == 'string') {
32680 if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32682 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32683 t = Roo.factory(t);
32685 this.toolbars[i] = t;
32686 this.toolbars[i].render(this.toolbarContainer());
32694 onRender : function(ct, position)
32696 // Roo.log("Call onRender: " + this.xtype);
32698 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32700 this.wrap = this.inputEl().wrap({
32701 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32704 this.editorcore.onRender(ct, position);
32707 this.createToolbar(this);
32715 onResize : function(w, h)
32717 Roo.log('resize: ' +w + ',' + h );
32718 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32722 if(this.inputEl() ){
32723 if(typeof w == 'number'){
32724 var aw = w - this.wrap.getFrameWidth('lr');
32725 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32728 if(typeof h == 'number'){
32729 var tbh = -11; // fixme it needs to tool bar size!
32730 for (var i =0; i < this.toolbars.length;i++) {
32731 // fixme - ask toolbars for heights?
32732 tbh += this.toolbars[i].el.getHeight();
32733 //if (this.toolbars[i].footer) {
32734 // tbh += this.toolbars[i].footer.el.getHeight();
32742 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32743 ah -= 5; // knock a few pixes off for look..
32744 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32748 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32749 this.editorcore.onResize(ew,eh);
32754 * Toggles the editor between standard and source edit mode.
32755 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32757 toggleSourceEdit : function(sourceEditMode)
32759 this.editorcore.toggleSourceEdit(sourceEditMode);
32761 if(this.editorcore.sourceEditMode){
32762 Roo.log('editor - showing textarea');
32765 // Roo.log(this.syncValue());
32767 this.inputEl().removeClass(['hide', 'x-hidden']);
32768 this.inputEl().dom.removeAttribute('tabIndex');
32769 this.inputEl().focus();
32771 Roo.log('editor - hiding textarea');
32773 // Roo.log(this.pushValue());
32776 this.inputEl().addClass(['hide', 'x-hidden']);
32777 this.inputEl().dom.setAttribute('tabIndex', -1);
32778 //this.deferFocus();
32781 //if(this.resizable){
32782 // this.setSize(this.wrap.getSize());
32785 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32788 // private (for BoxComponent)
32789 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32791 // private (for BoxComponent)
32792 getResizeEl : function(){
32796 // private (for BoxComponent)
32797 getPositionEl : function(){
32802 initEvents : function(){
32803 this.originalValue = this.getValue();
32807 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32810 // markInvalid : Roo.emptyFn,
32812 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32815 // clearInvalid : Roo.emptyFn,
32817 setValue : function(v){
32818 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32819 this.editorcore.pushValue();
32824 deferFocus : function(){
32825 this.focus.defer(10, this);
32829 focus : function(){
32830 this.editorcore.focus();
32836 onDestroy : function(){
32842 for (var i =0; i < this.toolbars.length;i++) {
32843 // fixme - ask toolbars for heights?
32844 this.toolbars[i].onDestroy();
32847 this.wrap.dom.innerHTML = '';
32848 this.wrap.remove();
32853 onFirstFocus : function(){
32854 //Roo.log("onFirstFocus");
32855 this.editorcore.onFirstFocus();
32856 for (var i =0; i < this.toolbars.length;i++) {
32857 this.toolbars[i].onFirstFocus();
32863 syncValue : function()
32865 this.editorcore.syncValue();
32868 pushValue : function()
32870 this.editorcore.pushValue();
32874 // hide stuff that is not compatible
32888 * @event specialkey
32892 * @cfg {String} fieldClass @hide
32895 * @cfg {String} focusClass @hide
32898 * @cfg {String} autoCreate @hide
32901 * @cfg {String} inputType @hide
32905 * @cfg {String} invalidText @hide
32908 * @cfg {String} msgFx @hide
32911 * @cfg {String} validateOnBlur @hide
32921 * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32922 * @parent Roo.bootstrap.form.HtmlEditor
32923 * @extends Roo.bootstrap.nav.Simplebar
32929 new Roo.bootstrap.form.HtmlEditor({
32932 new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32933 disable : { fonts: 1 , format: 1, ..., ... , ...],
32939 * @cfg {Object} disable List of elements to disable..
32940 * @cfg {Array} btns List of additional buttons.
32944 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32947 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32950 Roo.apply(this, config);
32952 // default disabled, based on 'good practice'..
32953 this.disable = this.disable || {};
32954 Roo.applyIf(this.disable, {
32957 specialElements : true
32959 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32961 this.editor = config.editor;
32962 this.editorcore = config.editor.editorcore;
32964 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32966 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32967 // dont call parent... till later.
32969 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar, {
32974 editorcore : false,
32979 "h1","h2","h3","h4","h5","h6",
32981 "abbr", "acronym", "address", "cite", "samp", "var",
32988 onRender : function(ct, position)
32990 // Roo.log("Call onRender: " + this.xtype);
32992 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
32994 this.el.dom.style.marginBottom = '0';
32996 var editorcore = this.editorcore;
32997 var editor= this.editor;
33000 var btn = function(id, cmd , toggle, handler, html){
33002 var event = toggle ? 'toggle' : 'click';
33007 xns: Roo.bootstrap,
33011 cls : 'roo-html-editor-btn-' + id,
33012 cmd : cmd, // why id || cmd
33013 enableToggle: toggle !== false,
33015 pressed : toggle ? false : null,
33018 a.listeners[toggle ? 'toggle' : 'click'] = function() {
33019 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
33025 // var cb_box = function...
33030 xns: Roo.bootstrap,
33032 cls : 'roo-html-editor-font-chooser',
33036 xns: Roo.bootstrap,
33040 Roo.each(this.formats, function(f) {
33041 style.menu.items.push({
33043 xns: Roo.bootstrap,
33044 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33049 editorcore.insertTag(this.tagname);
33056 children.push(style);
33058 btn('bold', 'bold',true);
33059 btn('italic', 'italic',true);
33060 btn('underline', 'underline',true);
33061 btn('align-left', 'justifyleft',true);
33062 btn('align-center', 'justifycenter',true);
33063 btn('align-right' , 'justifyright',true);
33064 btn('link', false, true, this.onLinkClick);
33067 btn('image', false, true, this.onImageClick);
33068 btn('list','insertunorderedlist',true);
33069 btn('list-ol','insertorderedlist',true);
33071 btn('pencil', false,true, function(btn){
33073 this.toggleSourceEdit(btn.pressed);
33076 if (this.editor.btns.length > 0) {
33077 for (var i = 0; i<this.editor.btns.length; i++) {
33078 children.push(this.editor.btns[i]);
33084 this.xtype = 'NavSimplebar'; // why?
33086 for(var i=0;i< children.length;i++) {
33088 this.buttons.add(this.addxtypeChild(children[i]));
33091 this.buildToolbarDelete();
33093 editor.on('editorevent', this.updateToolbar, this);
33096 buildToolbarDelete : function()
33099 /* this.addxtypeChild({
33101 xns : Roo.bootstrap,
33102 cls : 'roo-htmleditor-fill'
33105 this.deleteBtn = this.addxtypeChild({
33108 xns: Roo.bootstrap,
33111 click : this.onDelete.createDelegate(this)
33114 this.deleteBtn.hide();
33118 onImageClick : function()
33121 this.input.un('change', this.onFileSelected, this);
33123 this.input = Roo.get(document.body).createChild({
33126 style : 'display:none',
33127 multiple: 'multiple'
33129 this.input.on('change', this.onFileSelected, this);
33130 this.input.dom.click();
33133 onFileSelected : function(e)
33135 e.preventDefault();
33137 if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33142 this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33145 addFiles : function(far, fire_add) {
33148 var editor = this.editorcore;
33152 this.editor.syncValue();
33153 editor.owner.fireEvent('editorevent', editor.owner, false);
33154 editor.owner.fireEvent('imageadd', editor.owner, false);
33161 if (!f.type.match(/^image/)) {
33162 this.addFiles(far, fire_add);
33166 var sn = this.selectedNode;
33168 var bl = sn && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33171 var reader = new FileReader();
33172 reader.addEventListener('load', (function() {
33174 bl.image_src = reader.result;
33175 //bl.caption = f.name;
33176 bl.updateElement(sn);
33177 this.editor.syncValue();
33178 editor.owner.fireEvent('editorevent', editor.owner, false);
33179 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33180 // we only do the first file!! and replace.
33183 if (this.editorcore.enableBlocks) {
33184 var fig = new Roo.htmleditor.BlockFigure({
33185 image_src : reader.result,
33187 caption_display : 'none' //default to hide captions..
33189 editor.insertAtCursor(fig.toHTML());
33190 this.addFiles(far, true);
33193 // just a standard img..
33194 if (sn && sn.tagName.toUpperCase() == 'IMG') {
33195 sn.src = reader.result;
33196 this.editor.syncValue();
33197 editor.owner.fireEvent('editorevent', editor.owner, false);
33198 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33201 editor.insertAtCursor('<img src="' + reader.result +'">');
33202 this.addFiles(far, true);
33204 }).createDelegate(this));
33205 reader.readAsDataURL(f);
33211 onBtnClick : function(id)
33213 this.editorcore.relayCmd(id);
33214 this.editorcore.focus();
33217 onLinkClick : function(btn) {
33218 var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33219 this.selectedNode.getAttribute('href') : '';
33221 Roo.bootstrap.MessageBox.show({
33222 title : "Add / Edit Link URL",
33223 msg : "Enter the URL for the link",
33224 buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33231 fn: function(pressed, newurl) {
33232 if (pressed != 'ok') {
33233 this.editorcore.focus();
33237 this.selectedNode.setAttribute('href', newurl);
33240 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33241 this.editorcore.relayCmd('createlink', newurl);
33243 this.editorcore.focus();
33248 * Protected method that will not generally be called directly. It triggers
33249 * a toolbar update by reading the markup state of the current selection in the editor.
33251 updateToolbar: function(editor ,ev, sel){
33253 if(!this.editorcore.activated){
33254 this.editor.onFirstFocus(); // is this neeed?
33258 var btns = this.buttons;
33259 var doc = this.editorcore.doc;
33260 var hasToggle = false;
33261 btns.each(function(e) {
33262 if (e.enableToggle && e.cmd) {
33263 hasToggle = hasToggle || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33264 e.setActive(doc.queryCommandState(e.cmd));
33270 (ev.type == 'mouseup' || ev.type == 'click' ) &&
33271 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33272 // they have click on an image...
33273 // let's see if we can change the selection...
33278 var ans = this.editorcore.getAllAncestors();
33280 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
33281 sel = sel ? sel : this.editorcore.doc.body;
33282 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33286 var lastSel = this.selectedNode;
33287 this.selectedNode = sel;
33289 // ok see if we are editing a block?
33292 // you are not actually selecting the block.
33293 if (sel && sel.hasAttribute('data-block')) {
33295 } else if (sel && sel.closest('[data-block]')) {
33296 db = sel.closest('[data-block]');
33299 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33300 e.classList.remove('roo-ed-selection');
33304 if (db && this.editorcore.enableBlocks) {
33305 block = Roo.htmleditor.Block.factory(db);
33308 db.className = (db.classList.length > 0 ? db.className + ' ' : '') +
33309 ' roo-ed-selection';
33310 sel = this.selectedNode = db;
33314 // highlight the 'a'..
33315 var tn = sel && sel.tagName.toUpperCase() || '';
33316 if (!block && sel && tn != 'A') {
33317 var asel = sel.closest('A');
33323 btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33324 btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33325 btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33327 Roo.bootstrap.menu.Manager.hideAll();
33333 // handle delete button..
33334 if (hasToggle || (tn.length && tn == 'BODY')) {
33335 this.deleteBtn.hide();
33339 this.deleteBtn.show();
33343 //this.editorsyncValue();
33345 onFirstFocus: function() {
33346 this.buttons.each(function(item){
33351 onDelete : function()
33353 var range = this.editorcore.createRange();
33354 var selection = this.editorcore.getSelection();
33355 var sn = this.selectedNode;
33356 range.setStart(sn,0);
33357 range.setEnd(sn,0);
33360 if (sn.hasAttribute('data-block')) {
33361 var block = Roo.htmleditor.Block.factory(this.selectedNode);
33363 sn = block.removeNode();
33364 sn.parentNode.removeChild(sn);
33365 selection.removeAllRanges();
33366 selection.addRange(range);
33367 this.updateToolbar(null, null, null);
33368 if (sn.tagName.toUpperCase() == 'FIGURE') {
33369 this.editor.syncValue();
33370 this.editor.fireEvent('imagedelete', this.editor, sn);
33373 this.selectedNode = false;
33374 this.editorcore.fireEditorEvent(false);
33380 return; // should not really happen..
33382 if (sn && sn.tagName == 'BODY') {
33385 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33387 // remove and keep parents.
33388 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33391 selection.removeAllRanges();
33392 selection.addRange(range);
33393 if (sn.tagName.toUpperCase() == 'IMG"') {
33394 this.editor.syncValue();
33395 this.editor.fireEvent('imagedelete', this.editor, sn);
33398 this.selectedNode = false;
33399 this.editorcore.fireEditorEvent(false);
33405 toggleSourceEdit : function(sourceEditMode){
33408 if(sourceEditMode){
33409 Roo.log("disabling buttons");
33410 this.buttons.each( function(item){
33411 if(item.cmd != 'pencil'){
33417 Roo.log("enabling buttons");
33418 if(this.editorcore.initialized){
33419 this.buttons.each( function(item){
33425 Roo.log("calling toggole on editor");
33426 // tell the editor that it's been pressed..
33427 this.editor.toggleSourceEdit(sourceEditMode);
33441 * @class Roo.bootstrap.form.Markdown
33442 * @extends Roo.bootstrap.form.TextArea
33443 * Bootstrap Showdown editable area
33444 * @cfg {string} content
33447 * Create a new Showdown
33450 Roo.bootstrap.form.Markdown = function(config){
33451 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33455 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
33459 initEvents : function()
33462 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33463 this.markdownEl = this.el.createChild({
33464 cls : 'roo-markdown-area'
33466 this.inputEl().addClass('d-none');
33467 if (this.getValue() == '') {
33468 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33471 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33473 this.markdownEl.on('click', this.toggleTextEdit, this);
33474 this.on('blur', this.toggleTextEdit, this);
33475 this.on('specialkey', this.resizeTextArea, this);
33478 toggleTextEdit : function()
33480 var sh = this.markdownEl.getHeight();
33481 this.inputEl().addClass('d-none');
33482 this.markdownEl.addClass('d-none');
33483 if (!this.editing) {
33485 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33486 this.inputEl().removeClass('d-none');
33487 this.inputEl().focus();
33488 this.editing = true;
33491 // show showdown...
33492 this.updateMarkdown();
33493 this.markdownEl.removeClass('d-none');
33494 this.editing = false;
33497 updateMarkdown : function()
33499 if (this.getValue() == '') {
33500 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33504 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33507 resizeTextArea: function () {
33510 Roo.log([sh, this.getValue().split("\n").length * 30]);
33511 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33513 setValue : function(val)
33515 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33516 if (!this.editing) {
33517 this.updateMarkdown();
33523 if (!this.editing) {
33524 this.toggleTextEdit();
33532 * Ext JS Library 1.1.1
33533 * Copyright(c) 2006-2007, Ext JS, LLC.
33535 * Originally Released Under LGPL - original licence link has changed is not relivant.
33538 * <script type="text/javascript">
33542 * @class Roo.bootstrap.PagingToolbar
33543 * @extends Roo.bootstrap.nav.Simplebar
33544 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33546 * Create a new PagingToolbar
33547 * @param {Object} config The config object
33548 * @param {Roo.data.Store} store
33550 Roo.bootstrap.PagingToolbar = function(config)
33552 // old args format still supported... - xtype is prefered..
33553 // created from xtype...
33555 this.ds = config.dataSource;
33557 if (config.store && !this.ds) {
33558 this.store= Roo.factory(config.store, Roo.data);
33559 this.ds = this.store;
33560 this.ds.xmodule = this.xmodule || false;
33563 this.toolbarItems = [];
33564 if (config.items) {
33565 this.toolbarItems = config.items;
33568 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33573 this.bind(this.ds);
33576 if (Roo.bootstrap.version == 4) {
33577 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33579 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33584 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33586 * @cfg {Roo.bootstrap.Button} buttons[]
33587 * Buttons for the toolbar
33590 * @cfg {Roo.data.Store} store
33591 * The underlying data store providing the paged data
33594 * @cfg {String/HTMLElement/Element} container
33595 * container The id or element that will contain the toolbar
33598 * @cfg {Boolean} displayInfo
33599 * True to display the displayMsg (defaults to false)
33602 * @cfg {Number} pageSize
33603 * The number of records to display per page (defaults to 20)
33607 * @cfg {String} displayMsg
33608 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33610 displayMsg : 'Displaying {0} - {1} of {2}',
33612 * @cfg {String} emptyMsg
33613 * The message to display when no records are found (defaults to "No data to display")
33615 emptyMsg : 'No data to display',
33617 * Customizable piece of the default paging text (defaults to "Page")
33620 beforePageText : "Page",
33622 * Customizable piece of the default paging text (defaults to "of %0")
33625 afterPageText : "of {0}",
33627 * Customizable piece of the default paging text (defaults to "First Page")
33630 firstText : "First Page",
33632 * Customizable piece of the default paging text (defaults to "Previous Page")
33635 prevText : "Previous Page",
33637 * Customizable piece of the default paging text (defaults to "Next Page")
33640 nextText : "Next Page",
33642 * Customizable piece of the default paging text (defaults to "Last Page")
33645 lastText : "Last Page",
33647 * Customizable piece of the default paging text (defaults to "Refresh")
33650 refreshText : "Refresh",
33654 onRender : function(ct, position)
33656 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33657 this.navgroup.parentId = this.id;
33658 this.navgroup.onRender(this.el, null);
33659 // add the buttons to the navgroup
33661 if(this.displayInfo){
33662 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33663 this.displayEl = this.el.select('.x-paging-info', true).first();
33664 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33665 // this.displayEl = navel.el.select('span',true).first();
33671 Roo.each(_this.buttons, function(e){ // this might need to use render????
33672 Roo.factory(e).render(_this.el);
33676 Roo.each(_this.toolbarItems, function(e) {
33677 _this.navgroup.addItem(e);
33681 this.first = this.navgroup.addItem({
33682 tooltip: this.firstText,
33683 cls: "prev btn-outline-secondary",
33684 html : ' <i class="fa fa-step-backward"></i>',
33686 preventDefault: true,
33687 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33690 this.prev = this.navgroup.addItem({
33691 tooltip: this.prevText,
33692 cls: "prev btn-outline-secondary",
33693 html : ' <i class="fa fa-backward"></i>',
33695 preventDefault: true,
33696 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
33698 //this.addSeparator();
33701 var field = this.navgroup.addItem( {
33703 cls : 'x-paging-position btn-outline-secondary',
33705 html : this.beforePageText +
33706 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33707 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
33710 this.field = field.el.select('input', true).first();
33711 this.field.on("keydown", this.onPagingKeydown, this);
33712 this.field.on("focus", function(){this.dom.select();});
33715 this.afterTextEl = field.el.select('.x-paging-after',true).first();
33716 //this.field.setHeight(18);
33717 //this.addSeparator();
33718 this.next = this.navgroup.addItem({
33719 tooltip: this.nextText,
33720 cls: "next btn-outline-secondary",
33721 html : ' <i class="fa fa-forward"></i>',
33723 preventDefault: true,
33724 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
33726 this.last = this.navgroup.addItem({
33727 tooltip: this.lastText,
33728 html : ' <i class="fa fa-step-forward"></i>',
33729 cls: "next btn-outline-secondary",
33731 preventDefault: true,
33732 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
33734 //this.addSeparator();
33735 this.loading = this.navgroup.addItem({
33736 tooltip: this.refreshText,
33737 cls: "btn-outline-secondary",
33738 html : ' <i class="fa fa-refresh"></i>',
33739 preventDefault: true,
33740 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33746 updateInfo : function(){
33747 if(this.displayEl){
33748 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33749 var msg = count == 0 ?
33753 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
33755 this.displayEl.update(msg);
33760 onLoad : function(ds, r, o)
33762 this.cursor = o.params && o.params.start ? o.params.start : 0;
33764 var d = this.getPageData(),
33769 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33770 this.field.dom.value = ap;
33771 this.first.setDisabled(ap == 1);
33772 this.prev.setDisabled(ap == 1);
33773 this.next.setDisabled(ap == ps);
33774 this.last.setDisabled(ap == ps);
33775 this.loading.enable();
33780 getPageData : function(){
33781 var total = this.ds.getTotalCount();
33784 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33785 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33790 onLoadError : function(proxy, o){
33791 this.loading.enable();
33792 if (this.ds.events.loadexception.listeners.length < 2) {
33793 // nothing has been assigned to loadexception except this...
33795 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33801 onPagingKeydown : function(e){
33802 var k = e.getKey();
33803 var d = this.getPageData();
33805 var v = this.field.dom.value, pageNum;
33806 if(!v || isNaN(pageNum = parseInt(v, 10))){
33807 this.field.dom.value = d.activePage;
33810 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33811 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33814 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))
33816 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33817 this.field.dom.value = pageNum;
33818 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33821 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33823 var v = this.field.dom.value, pageNum;
33824 var increment = (e.shiftKey) ? 10 : 1;
33825 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33828 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33829 this.field.dom.value = d.activePage;
33832 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33834 this.field.dom.value = parseInt(v, 10) + increment;
33835 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33836 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33843 beforeLoad : function(){
33845 this.loading.disable();
33850 onClick : function(which){
33859 ds.load({params:{start: 0, limit: this.pageSize}});
33862 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33865 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33868 var total = ds.getTotalCount();
33869 var extra = total % this.pageSize;
33870 var lastStart = extra ? (total - extra) : total-this.pageSize;
33871 ds.load({params:{start: lastStart, limit: this.pageSize}});
33874 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33880 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33881 * @param {Roo.data.Store} store The data store to unbind
33883 unbind : function(ds){
33884 ds.un("beforeload", this.beforeLoad, this);
33885 ds.un("load", this.onLoad, this);
33886 ds.un("loadexception", this.onLoadError, this);
33887 ds.un("remove", this.updateInfo, this);
33888 ds.un("add", this.updateInfo, this);
33889 this.ds = undefined;
33893 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33894 * @param {Roo.data.Store} store The data store to bind
33896 bind : function(ds){
33897 ds.on("beforeload", this.beforeLoad, this);
33898 ds.on("load", this.onLoad, this);
33899 ds.on("loadexception", this.onLoadError, this);
33900 ds.on("remove", this.updateInfo, this);
33901 ds.on("add", this.updateInfo, this);
33912 * @class Roo.bootstrap.MessageBar
33913 * @extends Roo.bootstrap.Component
33914 * Bootstrap MessageBar class
33915 * @cfg {String} html contents of the MessageBar
33916 * @cfg {String} weight (info | success | warning | danger) default info
33917 * @cfg {String} beforeClass insert the bar before the given class
33918 * @cfg {Boolean} closable (true | false) default false
33919 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33922 * Create a new Element
33923 * @param {Object} config The config object
33926 Roo.bootstrap.MessageBar = function(config){
33927 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33930 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33936 beforeClass: 'bootstrap-sticky-wrap',
33938 getAutoCreate : function(){
33942 cls: 'alert alert-dismissable alert-' + this.weight,
33947 html: this.html || ''
33953 cfg.cls += ' alert-messages-fixed';
33967 onRender : function(ct, position)
33969 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33972 var cfg = Roo.apply({}, this.getAutoCreate());
33976 cfg.cls += ' ' + this.cls;
33979 cfg.style = this.style;
33981 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33983 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33986 this.el.select('>button.close').on('click', this.hide, this);
33992 if (!this.rendered) {
33998 this.fireEvent('show', this);
34004 if (!this.rendered) {
34010 this.fireEvent('hide', this);
34013 update : function()
34015 // var e = this.el.dom.firstChild;
34017 // if(this.closable){
34018 // e = e.nextSibling;
34021 // e.data = this.html || '';
34023 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34039 * @class Roo.bootstrap.Graph
34040 * @extends Roo.bootstrap.Component
34041 * Bootstrap Graph class
34045 @cfg {String} graphtype bar | vbar | pie
34046 @cfg {number} g_x coodinator | centre x (pie)
34047 @cfg {number} g_y coodinator | centre y (pie)
34048 @cfg {number} g_r radius (pie)
34049 @cfg {number} g_height height of the chart (respected by all elements in the set)
34050 @cfg {number} g_width width of the chart (respected by all elements in the set)
34051 @cfg {Object} title The title of the chart
34054 -opts (object) options for the chart
34056 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34057 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34059 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.
34060 o stacked (boolean) whether or not to tread values as in a stacked bar chart
34062 o stretch (boolean)
34064 -opts (object) options for the pie
34067 o startAngle (number)
34068 o endAngle (number)
34072 * Create a new Input
34073 * @param {Object} config The config object
34076 Roo.bootstrap.Graph = function(config){
34077 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34083 * The img click event for the img.
34084 * @param {Roo.EventObject} e
34090 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
34101 //g_colors: this.colors,
34108 getAutoCreate : function(){
34119 onRender : function(ct,position){
34122 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34124 if (typeof(Raphael) == 'undefined') {
34125 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34129 this.raphael = Raphael(this.el.dom);
34131 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34132 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34133 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34134 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34136 r.text(160, 10, "Single Series Chart").attr(txtattr);
34137 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34138 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34139 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34141 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34142 r.barchart(330, 10, 300, 220, data1);
34143 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34144 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34147 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34148 // r.barchart(30, 30, 560, 250, xdata, {
34149 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34150 // axis : "0 0 1 1",
34151 // axisxlabels : xdata
34152 // //yvalues : cols,
34155 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34157 // this.load(null,xdata,{
34158 // axis : "0 0 1 1",
34159 // axisxlabels : xdata
34164 load : function(graphtype,xdata,opts)
34166 this.raphael.clear();
34168 graphtype = this.graphtype;
34173 var r = this.raphael,
34174 fin = function () {
34175 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34177 fout = function () {
34178 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34180 pfin = function() {
34181 this.sector.stop();
34182 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34185 this.label[0].stop();
34186 this.label[0].attr({ r: 7.5 });
34187 this.label[1].attr({ "font-weight": 800 });
34190 pfout = function() {
34191 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34194 this.label[0].animate({ r: 5 }, 500, "bounce");
34195 this.label[1].attr({ "font-weight": 400 });
34201 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34204 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34207 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
34208 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34210 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34217 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34222 setTitle: function(o)
34227 initEvents: function() {
34230 this.el.on('click', this.onClick, this);
34234 onClick : function(e)
34236 Roo.log('img onclick');
34237 this.fireEvent('click', this, e);
34243 Roo.bootstrap.dash = {};/*
34249 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34252 * @class Roo.bootstrap.dash.NumberBox
34253 * @extends Roo.bootstrap.Component
34254 * Bootstrap NumberBox class
34255 * @cfg {String} headline Box headline
34256 * @cfg {String} content Box content
34257 * @cfg {String} icon Box icon
34258 * @cfg {String} footer Footer text
34259 * @cfg {String} fhref Footer href
34262 * Create a new NumberBox
34263 * @param {Object} config The config object
34267 Roo.bootstrap.dash.NumberBox = function(config){
34268 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34272 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
34281 getAutoCreate : function(){
34285 cls : 'small-box ',
34293 cls : 'roo-headline',
34294 html : this.headline
34298 cls : 'roo-content',
34299 html : this.content
34313 cls : 'ion ' + this.icon
34322 cls : 'small-box-footer',
34323 href : this.fhref || '#',
34327 cfg.cn.push(footer);
34334 onRender : function(ct,position){
34335 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34342 setHeadline: function (value)
34344 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34347 setFooter: function (value, href)
34349 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34352 this.el.select('a.small-box-footer',true).first().attr('href', href);
34357 setContent: function (value)
34359 this.el.select('.roo-content',true).first().dom.innerHTML = value;
34362 initEvents: function()
34376 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34379 * @class Roo.bootstrap.dash.TabBox
34380 * @extends Roo.bootstrap.Component
34381 * @children Roo.bootstrap.dash.TabPane
34382 * Bootstrap TabBox class
34383 * @cfg {String} title Title of the TabBox
34384 * @cfg {String} icon Icon of the TabBox
34385 * @cfg {Boolean} showtabs (true|false) show the tabs default true
34386 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34389 * Create a new TabBox
34390 * @param {Object} config The config object
34394 Roo.bootstrap.dash.TabBox = function(config){
34395 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34400 * When a pane is added
34401 * @param {Roo.bootstrap.dash.TabPane} pane
34405 * @event activatepane
34406 * When a pane is activated
34407 * @param {Roo.bootstrap.dash.TabPane} pane
34409 "activatepane" : true
34417 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
34422 tabScrollable : false,
34424 getChildContainer : function()
34426 return this.el.select('.tab-content', true).first();
34429 getAutoCreate : function(){
34433 cls: 'pull-left header',
34441 cls: 'fa ' + this.icon
34447 cls: 'nav nav-tabs pull-right',
34453 if(this.tabScrollable){
34460 cls: 'nav nav-tabs pull-right',
34471 cls: 'nav-tabs-custom',
34476 cls: 'tab-content no-padding',
34484 initEvents : function()
34486 //Roo.log('add add pane handler');
34487 this.on('addpane', this.onAddPane, this);
34490 * Updates the box title
34491 * @param {String} html to set the title to.
34493 setTitle : function(value)
34495 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34497 onAddPane : function(pane)
34499 this.panes.push(pane);
34500 //Roo.log('addpane');
34502 // tabs are rendere left to right..
34503 if(!this.showtabs){
34507 var ctr = this.el.select('.nav-tabs', true).first();
34510 var existing = ctr.select('.nav-tab',true);
34511 var qty = existing.getCount();;
34514 var tab = ctr.createChild({
34516 cls : 'nav-tab' + (qty ? '' : ' active'),
34524 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34527 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34529 pane.el.addClass('active');
34534 onTabClick : function(ev,un,ob,pane)
34536 //Roo.log('tab - prev default');
34537 ev.preventDefault();
34540 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34541 pane.tab.addClass('active');
34542 //Roo.log(pane.title);
34543 this.getChildContainer().select('.tab-pane',true).removeClass('active');
34544 // technically we should have a deactivate event.. but maybe add later.
34545 // and it should not de-activate the selected tab...
34546 this.fireEvent('activatepane', pane);
34547 pane.el.addClass('active');
34548 pane.fireEvent('activate');
34553 getActivePane : function()
34556 Roo.each(this.panes, function(p) {
34557 if(p.el.hasClass('active')){
34578 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34580 * @class Roo.bootstrap.TabPane
34581 * @extends Roo.bootstrap.Component
34582 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
34583 * Bootstrap TabPane class
34584 * @cfg {Boolean} active (false | true) Default false
34585 * @cfg {String} title title of panel
34589 * Create a new TabPane
34590 * @param {Object} config The config object
34593 Roo.bootstrap.dash.TabPane = function(config){
34594 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34600 * When a pane is activated
34601 * @param {Roo.bootstrap.dash.TabPane} pane
34608 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
34613 // the tabBox that this is attached to.
34616 getAutoCreate : function()
34624 cfg.cls += ' active';
34629 initEvents : function()
34631 //Roo.log('trigger add pane handler');
34632 this.parent().fireEvent('addpane', this)
34636 * Updates the tab title
34637 * @param {String} html to set the title to.
34639 setTitle: function(str)
34645 this.tab.select('a', true).first().dom.innerHTML = str;
34664 * @class Roo.bootstrap.Tooltip
34665 * Bootstrap Tooltip class
34666 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34667 * to determine which dom element triggers the tooltip.
34669 * It needs to add support for additional attributes like tooltip-position
34672 * Create a new Toolti
34673 * @param {Object} config The config object
34676 Roo.bootstrap.Tooltip = function(config){
34677 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34679 this.alignment = Roo.bootstrap.Tooltip.alignment;
34681 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34682 this.alignment = config.alignment;
34687 Roo.apply(Roo.bootstrap.Tooltip, {
34689 * @function init initialize tooltip monitoring.
34693 currentTip : false,
34694 currentRegion : false,
34700 Roo.get(document).on('mouseover', this.enter ,this);
34701 Roo.get(document).on('mouseout', this.leave, this);
34704 this.currentTip = new Roo.bootstrap.Tooltip();
34707 enter : function(ev)
34709 var dom = ev.getTarget();
34711 //Roo.log(['enter',dom]);
34712 var el = Roo.fly(dom);
34713 if (this.currentEl) {
34715 //Roo.log(this.currentEl);
34716 //Roo.log(this.currentEl.contains(dom));
34717 if (this.currentEl == el) {
34720 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34726 if (this.currentTip.el) {
34727 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34731 if(!el || el.dom == document){
34737 if (!el.attr('tooltip')) {
34738 pel = el.findParent("[tooltip]");
34740 bindEl = Roo.get(pel);
34746 // you can not look for children, as if el is the body.. then everythign is the child..
34747 if (!pel && !el.attr('tooltip')) { //
34748 if (!el.select("[tooltip]").elements.length) {
34751 // is the mouse over this child...?
34752 bindEl = el.select("[tooltip]").first();
34753 var xy = ev.getXY();
34754 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34755 //Roo.log("not in region.");
34758 //Roo.log("child element over..");
34761 this.currentEl = el;
34762 this.currentTip.bind(bindEl);
34763 this.currentRegion = Roo.lib.Region.getRegion(dom);
34764 this.currentTip.enter();
34767 leave : function(ev)
34769 var dom = ev.getTarget();
34770 //Roo.log(['leave',dom]);
34771 if (!this.currentEl) {
34776 if (dom != this.currentEl.dom) {
34779 var xy = ev.getXY();
34780 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
34783 // only activate leave if mouse cursor is outside... bounding box..
34788 if (this.currentTip) {
34789 this.currentTip.leave();
34791 //Roo.log('clear currentEl');
34792 this.currentEl = false;
34797 'left' : ['r-l', [-2,0], 'right'],
34798 'right' : ['l-r', [2,0], 'left'],
34799 'bottom' : ['t-b', [0,2], 'top'],
34800 'top' : [ 'b-t', [0,-2], 'bottom']
34806 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
34811 delay : null, // can be { show : 300 , hide: 500}
34815 hoverState : null, //???
34817 placement : 'bottom',
34821 getAutoCreate : function(){
34828 cls : 'tooltip-arrow arrow'
34831 cls : 'tooltip-inner'
34838 bind : function(el)
34843 initEvents : function()
34845 this.arrowEl = this.el.select('.arrow', true).first();
34846 this.innerEl = this.el.select('.tooltip-inner', true).first();
34849 enter : function () {
34851 if (this.timeout != null) {
34852 clearTimeout(this.timeout);
34855 this.hoverState = 'in';
34856 //Roo.log("enter - show");
34857 if (!this.delay || !this.delay.show) {
34862 this.timeout = setTimeout(function () {
34863 if (_t.hoverState == 'in') {
34866 }, this.delay.show);
34870 clearTimeout(this.timeout);
34872 this.hoverState = 'out';
34873 if (!this.delay || !this.delay.hide) {
34879 this.timeout = setTimeout(function () {
34880 //Roo.log("leave - timeout");
34882 if (_t.hoverState == 'out') {
34884 Roo.bootstrap.Tooltip.currentEl = false;
34889 show : function (msg)
34892 this.render(document.body);
34895 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34897 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34899 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34901 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34902 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34904 if(this.bindEl.attr('tooltip-class')) {
34905 this.el.addClass(this.bindEl.attr('tooltip-class'));
34908 var placement = typeof this.placement == 'function' ?
34909 this.placement.call(this, this.el, on_el) :
34912 if(this.bindEl.attr('tooltip-placement')) {
34913 placement = this.bindEl.attr('tooltip-placement');
34916 var autoToken = /\s?auto?\s?/i;
34917 var autoPlace = autoToken.test(placement);
34919 placement = placement.replace(autoToken, '') || 'top';
34923 //this.el.setXY([0,0]);
34925 //this.el.dom.style.display='block';
34927 //this.el.appendTo(on_el);
34929 var p = this.getPosition();
34930 var box = this.el.getBox();
34936 var align = this.alignment[placement];
34938 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34940 if(placement == 'top' || placement == 'bottom'){
34942 placement = 'right';
34945 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34946 placement = 'left';
34949 var scroll = Roo.select('body', true).first().getScroll();
34951 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34955 align = this.alignment[placement];
34957 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34961 var elems = document.getElementsByTagName('div');
34962 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34963 for (var i = 0; i < elems.length; i++) {
34964 var zindex = Number.parseInt(
34965 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34968 if (zindex > highest) {
34975 this.el.dom.style.zIndex = highest;
34977 this.el.alignTo(this.bindEl, align[0],align[1]);
34978 //var arrow = this.el.select('.arrow',true).first();
34979 //arrow.set(align[2],
34981 this.el.addClass(placement);
34982 this.el.addClass("bs-tooltip-"+ placement);
34984 this.el.addClass('in fade show');
34986 this.hoverState = null;
34988 if (this.el.hasClass('fade')) {
35003 //this.el.setXY([0,0]);
35004 if(this.bindEl.attr('tooltip-class')) {
35005 this.el.removeClass(this.bindEl.attr('tooltip-class'));
35007 this.el.removeClass(['show', 'in']);
35023 * @class Roo.bootstrap.LocationPicker
35024 * @extends Roo.bootstrap.Component
35025 * Bootstrap LocationPicker class
35026 * @cfg {Number} latitude Position when init default 0
35027 * @cfg {Number} longitude Position when init default 0
35028 * @cfg {Number} zoom default 15
35029 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35030 * @cfg {Boolean} mapTypeControl default false
35031 * @cfg {Boolean} disableDoubleClickZoom default false
35032 * @cfg {Boolean} scrollwheel default true
35033 * @cfg {Boolean} streetViewControl default false
35034 * @cfg {Number} radius default 0
35035 * @cfg {String} locationName
35036 * @cfg {Boolean} draggable default true
35037 * @cfg {Boolean} enableAutocomplete default false
35038 * @cfg {Boolean} enableReverseGeocode default true
35039 * @cfg {String} markerTitle
35042 * Create a new LocationPicker
35043 * @param {Object} config The config object
35047 Roo.bootstrap.LocationPicker = function(config){
35049 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35054 * Fires when the picker initialized.
35055 * @param {Roo.bootstrap.LocationPicker} this
35056 * @param {Google Location} location
35060 * @event positionchanged
35061 * Fires when the picker position changed.
35062 * @param {Roo.bootstrap.LocationPicker} this
35063 * @param {Google Location} location
35065 positionchanged : true,
35068 * Fires when the map resize.
35069 * @param {Roo.bootstrap.LocationPicker} this
35074 * Fires when the map show.
35075 * @param {Roo.bootstrap.LocationPicker} this
35080 * Fires when the map hide.
35081 * @param {Roo.bootstrap.LocationPicker} this
35086 * Fires when click the map.
35087 * @param {Roo.bootstrap.LocationPicker} this
35088 * @param {Map event} e
35092 * @event mapRightClick
35093 * Fires when right click the map.
35094 * @param {Roo.bootstrap.LocationPicker} this
35095 * @param {Map event} e
35097 mapRightClick : true,
35099 * @event markerClick
35100 * Fires when click the marker.
35101 * @param {Roo.bootstrap.LocationPicker} this
35102 * @param {Map event} e
35104 markerClick : true,
35106 * @event markerRightClick
35107 * Fires when right click the marker.
35108 * @param {Roo.bootstrap.LocationPicker} this
35109 * @param {Map event} e
35111 markerRightClick : true,
35113 * @event OverlayViewDraw
35114 * Fires when OverlayView Draw
35115 * @param {Roo.bootstrap.LocationPicker} this
35117 OverlayViewDraw : true,
35119 * @event OverlayViewOnAdd
35120 * Fires when OverlayView Draw
35121 * @param {Roo.bootstrap.LocationPicker} this
35123 OverlayViewOnAdd : true,
35125 * @event OverlayViewOnRemove
35126 * Fires when OverlayView Draw
35127 * @param {Roo.bootstrap.LocationPicker} this
35129 OverlayViewOnRemove : true,
35131 * @event OverlayViewShow
35132 * Fires when OverlayView Draw
35133 * @param {Roo.bootstrap.LocationPicker} this
35134 * @param {Pixel} cpx
35136 OverlayViewShow : true,
35138 * @event OverlayViewHide
35139 * Fires when OverlayView Draw
35140 * @param {Roo.bootstrap.LocationPicker} this
35142 OverlayViewHide : true,
35144 * @event loadexception
35145 * Fires when load google lib failed.
35146 * @param {Roo.bootstrap.LocationPicker} this
35148 loadexception : true
35153 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
35155 gMapContext: false,
35161 mapTypeControl: false,
35162 disableDoubleClickZoom: false,
35164 streetViewControl: false,
35168 enableAutocomplete: false,
35169 enableReverseGeocode: true,
35172 getAutoCreate: function()
35177 cls: 'roo-location-picker'
35183 initEvents: function(ct, position)
35185 if(!this.el.getWidth() || this.isApplied()){
35189 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35194 initial: function()
35196 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35197 this.fireEvent('loadexception', this);
35201 if(!this.mapTypeId){
35202 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35205 this.gMapContext = this.GMapContext();
35207 this.initOverlayView();
35209 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35213 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35214 _this.setPosition(_this.gMapContext.marker.position);
35217 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35218 _this.fireEvent('mapClick', this, event);
35222 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35223 _this.fireEvent('mapRightClick', this, event);
35227 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35228 _this.fireEvent('markerClick', this, event);
35232 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35233 _this.fireEvent('markerRightClick', this, event);
35237 this.setPosition(this.gMapContext.location);
35239 this.fireEvent('initial', this, this.gMapContext.location);
35242 initOverlayView: function()
35246 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35250 _this.fireEvent('OverlayViewDraw', _this);
35255 _this.fireEvent('OverlayViewOnAdd', _this);
35258 onRemove: function()
35260 _this.fireEvent('OverlayViewOnRemove', _this);
35263 show: function(cpx)
35265 _this.fireEvent('OverlayViewShow', _this, cpx);
35270 _this.fireEvent('OverlayViewHide', _this);
35276 fromLatLngToContainerPixel: function(event)
35278 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35281 isApplied: function()
35283 return this.getGmapContext() == false ? false : true;
35286 getGmapContext: function()
35288 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35291 GMapContext: function()
35293 var position = new google.maps.LatLng(this.latitude, this.longitude);
35295 var _map = new google.maps.Map(this.el.dom, {
35298 mapTypeId: this.mapTypeId,
35299 mapTypeControl: this.mapTypeControl,
35300 disableDoubleClickZoom: this.disableDoubleClickZoom,
35301 scrollwheel: this.scrollwheel,
35302 streetViewControl: this.streetViewControl,
35303 locationName: this.locationName,
35304 draggable: this.draggable,
35305 enableAutocomplete: this.enableAutocomplete,
35306 enableReverseGeocode: this.enableReverseGeocode
35309 var _marker = new google.maps.Marker({
35310 position: position,
35312 title: this.markerTitle,
35313 draggable: this.draggable
35320 location: position,
35321 radius: this.radius,
35322 locationName: this.locationName,
35323 addressComponents: {
35324 formatted_address: null,
35325 addressLine1: null,
35326 addressLine2: null,
35328 streetNumber: null,
35332 stateOrProvince: null
35335 domContainer: this.el.dom,
35336 geodecoder: new google.maps.Geocoder()
35340 drawCircle: function(center, radius, options)
35342 if (this.gMapContext.circle != null) {
35343 this.gMapContext.circle.setMap(null);
35347 options = Roo.apply({}, options, {
35348 strokeColor: "#0000FF",
35349 strokeOpacity: .35,
35351 fillColor: "#0000FF",
35355 options.map = this.gMapContext.map;
35356 options.radius = radius;
35357 options.center = center;
35358 this.gMapContext.circle = new google.maps.Circle(options);
35359 return this.gMapContext.circle;
35365 setPosition: function(location)
35367 this.gMapContext.location = location;
35368 this.gMapContext.marker.setPosition(location);
35369 this.gMapContext.map.panTo(location);
35370 this.drawCircle(location, this.gMapContext.radius, {});
35374 if (this.gMapContext.settings.enableReverseGeocode) {
35375 this.gMapContext.geodecoder.geocode({
35376 latLng: this.gMapContext.location
35377 }, function(results, status) {
35379 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35380 _this.gMapContext.locationName = results[0].formatted_address;
35381 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35383 _this.fireEvent('positionchanged', this, location);
35390 this.fireEvent('positionchanged', this, location);
35395 google.maps.event.trigger(this.gMapContext.map, "resize");
35397 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35399 this.fireEvent('resize', this);
35402 setPositionByLatLng: function(latitude, longitude)
35404 this.setPosition(new google.maps.LatLng(latitude, longitude));
35407 getCurrentPosition: function()
35410 latitude: this.gMapContext.location.lat(),
35411 longitude: this.gMapContext.location.lng()
35415 getAddressName: function()
35417 return this.gMapContext.locationName;
35420 getAddressComponents: function()
35422 return this.gMapContext.addressComponents;
35425 address_component_from_google_geocode: function(address_components)
35429 for (var i = 0; i < address_components.length; i++) {
35430 var component = address_components[i];
35431 if (component.types.indexOf("postal_code") >= 0) {
35432 result.postalCode = component.short_name;
35433 } else if (component.types.indexOf("street_number") >= 0) {
35434 result.streetNumber = component.short_name;
35435 } else if (component.types.indexOf("route") >= 0) {
35436 result.streetName = component.short_name;
35437 } else if (component.types.indexOf("neighborhood") >= 0) {
35438 result.city = component.short_name;
35439 } else if (component.types.indexOf("locality") >= 0) {
35440 result.city = component.short_name;
35441 } else if (component.types.indexOf("sublocality") >= 0) {
35442 result.district = component.short_name;
35443 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35444 result.stateOrProvince = component.short_name;
35445 } else if (component.types.indexOf("country") >= 0) {
35446 result.country = component.short_name;
35450 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35451 result.addressLine2 = "";
35455 setZoomLevel: function(zoom)
35457 this.gMapContext.map.setZoom(zoom);
35470 this.fireEvent('show', this);
35481 this.fireEvent('hide', this);
35486 Roo.apply(Roo.bootstrap.LocationPicker, {
35488 OverlayView : function(map, options)
35490 options = options || {};
35497 * @class Roo.bootstrap.Alert
35498 * @extends Roo.bootstrap.Component
35499 * Bootstrap Alert class - shows an alert area box
35501 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35502 Enter a valid email address
35505 * @cfg {String} title The title of alert
35506 * @cfg {String} html The content of alert
35507 * @cfg {String} weight (success|info|warning|danger) Weight of the message
35508 * @cfg {String} fa font-awesomeicon
35509 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35510 * @cfg {Boolean} close true to show a x closer
35514 * Create a new alert
35515 * @param {Object} config The config object
35519 Roo.bootstrap.Alert = function(config){
35520 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35524 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
35530 faicon: false, // BC
35534 getAutoCreate : function()
35546 style : this.close ? '' : 'display:none'
35550 cls : 'roo-alert-icon'
35555 cls : 'roo-alert-title',
35560 cls : 'roo-alert-text',
35567 cfg.cn[0].cls += ' fa ' + this.faicon;
35570 cfg.cn[0].cls += ' fa ' + this.fa;
35574 cfg.cls += ' alert-' + this.weight;
35580 initEvents: function()
35582 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35583 this.titleEl = this.el.select('.roo-alert-title',true).first();
35584 this.iconEl = this.el.select('.roo-alert-icon',true).first();
35585 this.htmlEl = this.el.select('.roo-alert-text',true).first();
35586 if (this.seconds > 0) {
35587 this.hide.defer(this.seconds, this);
35591 * Set the Title Message HTML
35592 * @param {String} html
35594 setTitle : function(str)
35596 this.titleEl.dom.innerHTML = str;
35600 * Set the Body Message HTML
35601 * @param {String} html
35603 setHtml : function(str)
35605 this.htmlEl.dom.innerHTML = str;
35608 * Set the Weight of the alert
35609 * @param {String} (success|info|warning|danger) weight
35612 setWeight : function(weight)
35615 this.el.removeClass('alert-' + this.weight);
35618 this.weight = weight;
35620 this.el.addClass('alert-' + this.weight);
35623 * Set the Icon of the alert
35624 * @param {String} see fontawsome names (name without the 'fa-' bit)
35626 setIcon : function(icon)
35629 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35632 this.faicon = icon;
35634 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35659 * @class Roo.bootstrap.UploadCropbox
35660 * @extends Roo.bootstrap.Component
35661 * Bootstrap UploadCropbox class
35662 * @cfg {String} emptyText show when image has been loaded
35663 * @cfg {String} rotateNotify show when image too small to rotate
35664 * @cfg {Number} errorTimeout default 3000
35665 * @cfg {Number} minWidth default 300
35666 * @cfg {Number} minHeight default 300
35667 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35668 * @cfg {Boolean} isDocument (true|false) default false
35669 * @cfg {String} url action url
35670 * @cfg {String} paramName default 'imageUpload'
35671 * @cfg {String} method default POST
35672 * @cfg {Boolean} loadMask (true|false) default true
35673 * @cfg {Boolean} loadingText default 'Loading...'
35676 * Create a new UploadCropbox
35677 * @param {Object} config The config object
35680 Roo.bootstrap.UploadCropbox = function(config){
35681 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35685 * @event beforeselectfile
35686 * Fire before select file
35687 * @param {Roo.bootstrap.UploadCropbox} this
35689 "beforeselectfile" : true,
35692 * Fire after initEvent
35693 * @param {Roo.bootstrap.UploadCropbox} this
35698 * Fire after initEvent
35699 * @param {Roo.bootstrap.UploadCropbox} this
35700 * @param {String} data
35705 * Fire when preparing the file data
35706 * @param {Roo.bootstrap.UploadCropbox} this
35707 * @param {Object} file
35712 * Fire when get exception
35713 * @param {Roo.bootstrap.UploadCropbox} this
35714 * @param {XMLHttpRequest} xhr
35716 "exception" : true,
35718 * @event beforeloadcanvas
35719 * Fire before load the canvas
35720 * @param {Roo.bootstrap.UploadCropbox} this
35721 * @param {String} src
35723 "beforeloadcanvas" : true,
35726 * Fire when trash image
35727 * @param {Roo.bootstrap.UploadCropbox} this
35732 * Fire when download the image
35733 * @param {Roo.bootstrap.UploadCropbox} this
35737 * @event footerbuttonclick
35738 * Fire when footerbuttonclick
35739 * @param {Roo.bootstrap.UploadCropbox} this
35740 * @param {String} type
35742 "footerbuttonclick" : true,
35746 * @param {Roo.bootstrap.UploadCropbox} this
35751 * Fire when rotate the image
35752 * @param {Roo.bootstrap.UploadCropbox} this
35753 * @param {String} pos
35758 * Fire when inspect the file
35759 * @param {Roo.bootstrap.UploadCropbox} this
35760 * @param {Object} file
35765 * Fire when xhr upload the file
35766 * @param {Roo.bootstrap.UploadCropbox} this
35767 * @param {Object} data
35772 * Fire when arrange the file data
35773 * @param {Roo.bootstrap.UploadCropbox} this
35774 * @param {Object} formData
35779 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35782 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
35784 emptyText : 'Click to upload image',
35785 rotateNotify : 'Image is too small to rotate',
35786 errorTimeout : 3000,
35800 cropType : 'image/jpeg',
35802 canvasLoaded : false,
35803 isDocument : false,
35805 paramName : 'imageUpload',
35807 loadingText : 'Loading...',
35810 getAutoCreate : function()
35814 cls : 'roo-upload-cropbox',
35818 cls : 'roo-upload-cropbox-selector',
35823 cls : 'roo-upload-cropbox-body',
35824 style : 'cursor:pointer',
35828 cls : 'roo-upload-cropbox-preview'
35832 cls : 'roo-upload-cropbox-thumb'
35836 cls : 'roo-upload-cropbox-empty-notify',
35837 html : this.emptyText
35841 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35842 html : this.rotateNotify
35848 cls : 'roo-upload-cropbox-footer',
35851 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35861 onRender : function(ct, position)
35863 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35865 if (this.buttons.length) {
35867 Roo.each(this.buttons, function(bb) {
35869 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35871 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35877 this.maskEl = this.el;
35881 initEvents : function()
35883 this.urlAPI = (window.createObjectURL && window) ||
35884 (window.URL && URL.revokeObjectURL && URL) ||
35885 (window.webkitURL && webkitURL);
35887 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35888 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35890 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35891 this.selectorEl.hide();
35893 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35894 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35896 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35897 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35898 this.thumbEl.hide();
35900 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35901 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35903 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35904 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35905 this.errorEl.hide();
35907 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35908 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35909 this.footerEl.hide();
35911 this.setThumbBoxSize();
35917 this.fireEvent('initial', this);
35924 window.addEventListener("resize", function() { _this.resize(); } );
35926 this.bodyEl.on('click', this.beforeSelectFile, this);
35929 this.bodyEl.on('touchstart', this.onTouchStart, this);
35930 this.bodyEl.on('touchmove', this.onTouchMove, this);
35931 this.bodyEl.on('touchend', this.onTouchEnd, this);
35935 this.bodyEl.on('mousedown', this.onMouseDown, this);
35936 this.bodyEl.on('mousemove', this.onMouseMove, this);
35937 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35938 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35939 Roo.get(document).on('mouseup', this.onMouseUp, this);
35942 this.selectorEl.on('change', this.onFileSelected, this);
35948 this.baseScale = 1;
35950 this.baseRotate = 1;
35951 this.dragable = false;
35952 this.pinching = false;
35955 this.cropData = false;
35956 this.notifyEl.dom.innerHTML = this.emptyText;
35958 this.selectorEl.dom.value = '';
35962 resize : function()
35964 if(this.fireEvent('resize', this) != false){
35965 this.setThumbBoxPosition();
35966 this.setCanvasPosition();
35970 onFooterButtonClick : function(e, el, o, type)
35973 case 'rotate-left' :
35974 this.onRotateLeft(e);
35976 case 'rotate-right' :
35977 this.onRotateRight(e);
35980 this.beforeSelectFile(e);
35995 this.fireEvent('footerbuttonclick', this, type);
35998 beforeSelectFile : function(e)
36000 e.preventDefault();
36002 if(this.fireEvent('beforeselectfile', this) != false){
36003 this.selectorEl.dom.click();
36007 onFileSelected : function(e)
36009 e.preventDefault();
36011 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36015 var file = this.selectorEl.dom.files[0];
36017 if(this.fireEvent('inspect', this, file) != false){
36018 this.prepare(file);
36023 trash : function(e)
36025 this.fireEvent('trash', this);
36028 download : function(e)
36030 this.fireEvent('download', this);
36033 loadCanvas : function(src)
36035 if(this.fireEvent('beforeloadcanvas', this, src) != false){
36039 this.imageEl = document.createElement('img');
36043 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36045 this.imageEl.src = src;
36049 onLoadCanvas : function()
36051 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36052 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36054 this.bodyEl.un('click', this.beforeSelectFile, this);
36056 this.notifyEl.hide();
36057 this.thumbEl.show();
36058 this.footerEl.show();
36060 this.baseRotateLevel();
36062 if(this.isDocument){
36063 this.setThumbBoxSize();
36066 this.setThumbBoxPosition();
36068 this.baseScaleLevel();
36074 this.canvasLoaded = true;
36077 this.maskEl.unmask();
36082 setCanvasPosition : function()
36084 if(!this.canvasEl){
36088 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36089 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36091 this.previewEl.setLeft(pw);
36092 this.previewEl.setTop(ph);
36096 onMouseDown : function(e)
36100 this.dragable = true;
36101 this.pinching = false;
36103 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36104 this.dragable = false;
36108 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36109 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36113 onMouseMove : function(e)
36117 if(!this.canvasLoaded){
36121 if (!this.dragable){
36125 var minX = Math.ceil(this.thumbEl.getLeft(true));
36126 var minY = Math.ceil(this.thumbEl.getTop(true));
36128 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36129 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36131 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36132 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36134 x = x - this.mouseX;
36135 y = y - this.mouseY;
36137 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36138 var bgY = Math.ceil(y + this.previewEl.getTop(true));
36140 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36141 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36143 this.previewEl.setLeft(bgX);
36144 this.previewEl.setTop(bgY);
36146 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36147 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36150 onMouseUp : function(e)
36154 this.dragable = false;
36157 onMouseWheel : function(e)
36161 this.startScale = this.scale;
36163 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36165 if(!this.zoomable()){
36166 this.scale = this.startScale;
36175 zoomable : function()
36177 var minScale = this.thumbEl.getWidth() / this.minWidth;
36179 if(this.minWidth < this.minHeight){
36180 minScale = this.thumbEl.getHeight() / this.minHeight;
36183 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36184 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36188 (this.rotate == 0 || this.rotate == 180) &&
36190 width > this.imageEl.OriginWidth ||
36191 height > this.imageEl.OriginHeight ||
36192 (width < this.minWidth && height < this.minHeight)
36200 (this.rotate == 90 || this.rotate == 270) &&
36202 width > this.imageEl.OriginWidth ||
36203 height > this.imageEl.OriginHeight ||
36204 (width < this.minHeight && height < this.minWidth)
36211 !this.isDocument &&
36212 (this.rotate == 0 || this.rotate == 180) &&
36214 width < this.minWidth ||
36215 width > this.imageEl.OriginWidth ||
36216 height < this.minHeight ||
36217 height > this.imageEl.OriginHeight
36224 !this.isDocument &&
36225 (this.rotate == 90 || this.rotate == 270) &&
36227 width < this.minHeight ||
36228 width > this.imageEl.OriginWidth ||
36229 height < this.minWidth ||
36230 height > this.imageEl.OriginHeight
36240 onRotateLeft : function(e)
36242 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36244 var minScale = this.thumbEl.getWidth() / this.minWidth;
36246 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36247 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36249 this.startScale = this.scale;
36251 while (this.getScaleLevel() < minScale){
36253 this.scale = this.scale + 1;
36255 if(!this.zoomable()){
36260 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36261 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36266 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36273 this.scale = this.startScale;
36275 this.onRotateFail();
36280 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36282 if(this.isDocument){
36283 this.setThumbBoxSize();
36284 this.setThumbBoxPosition();
36285 this.setCanvasPosition();
36290 this.fireEvent('rotate', this, 'left');
36294 onRotateRight : function(e)
36296 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36298 var minScale = this.thumbEl.getWidth() / this.minWidth;
36300 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36301 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36303 this.startScale = this.scale;
36305 while (this.getScaleLevel() < minScale){
36307 this.scale = this.scale + 1;
36309 if(!this.zoomable()){
36314 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36315 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36320 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36327 this.scale = this.startScale;
36329 this.onRotateFail();
36334 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36336 if(this.isDocument){
36337 this.setThumbBoxSize();
36338 this.setThumbBoxPosition();
36339 this.setCanvasPosition();
36344 this.fireEvent('rotate', this, 'right');
36347 onRotateFail : function()
36349 this.errorEl.show(true);
36353 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36358 this.previewEl.dom.innerHTML = '';
36360 var canvasEl = document.createElement("canvas");
36362 var contextEl = canvasEl.getContext("2d");
36364 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36365 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36366 var center = this.imageEl.OriginWidth / 2;
36368 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36369 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36370 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36371 center = this.imageEl.OriginHeight / 2;
36374 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36376 contextEl.translate(center, center);
36377 contextEl.rotate(this.rotate * Math.PI / 180);
36379 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36381 this.canvasEl = document.createElement("canvas");
36383 this.contextEl = this.canvasEl.getContext("2d");
36385 switch (this.rotate) {
36388 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36389 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36391 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36396 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36397 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36399 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36400 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);
36404 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36409 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36410 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36412 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36413 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);
36417 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);
36422 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36423 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36425 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36426 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36430 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);
36437 this.previewEl.appendChild(this.canvasEl);
36439 this.setCanvasPosition();
36444 if(!this.canvasLoaded){
36448 var imageCanvas = document.createElement("canvas");
36450 var imageContext = imageCanvas.getContext("2d");
36452 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36453 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36455 var center = imageCanvas.width / 2;
36457 imageContext.translate(center, center);
36459 imageContext.rotate(this.rotate * Math.PI / 180);
36461 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36463 var canvas = document.createElement("canvas");
36465 var context = canvas.getContext("2d");
36467 canvas.width = this.minWidth;
36468 canvas.height = this.minHeight;
36470 switch (this.rotate) {
36473 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36474 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36476 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36477 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36479 var targetWidth = this.minWidth - 2 * x;
36480 var targetHeight = this.minHeight - 2 * y;
36484 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36485 scale = targetWidth / width;
36488 if(x > 0 && y == 0){
36489 scale = targetHeight / height;
36492 if(x > 0 && y > 0){
36493 scale = targetWidth / width;
36495 if(width < height){
36496 scale = targetHeight / height;
36500 context.scale(scale, scale);
36502 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36503 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36505 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36506 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36508 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36513 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36514 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36516 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36517 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36519 var targetWidth = this.minWidth - 2 * x;
36520 var targetHeight = this.minHeight - 2 * y;
36524 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36525 scale = targetWidth / width;
36528 if(x > 0 && y == 0){
36529 scale = targetHeight / height;
36532 if(x > 0 && y > 0){
36533 scale = targetWidth / width;
36535 if(width < height){
36536 scale = targetHeight / height;
36540 context.scale(scale, scale);
36542 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36543 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36545 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36546 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36548 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36550 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36555 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36556 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36558 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36559 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36561 var targetWidth = this.minWidth - 2 * x;
36562 var targetHeight = this.minHeight - 2 * y;
36566 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36567 scale = targetWidth / width;
36570 if(x > 0 && y == 0){
36571 scale = targetHeight / height;
36574 if(x > 0 && y > 0){
36575 scale = targetWidth / width;
36577 if(width < height){
36578 scale = targetHeight / height;
36582 context.scale(scale, scale);
36584 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36585 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36587 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36588 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36590 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36591 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36593 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36598 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36599 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36601 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36602 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36604 var targetWidth = this.minWidth - 2 * x;
36605 var targetHeight = this.minHeight - 2 * y;
36609 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36610 scale = targetWidth / width;
36613 if(x > 0 && y == 0){
36614 scale = targetHeight / height;
36617 if(x > 0 && y > 0){
36618 scale = targetWidth / width;
36620 if(width < height){
36621 scale = targetHeight / height;
36625 context.scale(scale, scale);
36627 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36628 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36630 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36631 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36633 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36635 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36642 this.cropData = canvas.toDataURL(this.cropType);
36644 if(this.fireEvent('crop', this, this.cropData) !== false){
36645 this.process(this.file, this.cropData);
36652 setThumbBoxSize : function()
36656 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36657 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36658 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36660 this.minWidth = width;
36661 this.minHeight = height;
36663 if(this.rotate == 90 || this.rotate == 270){
36664 this.minWidth = height;
36665 this.minHeight = width;
36670 width = Math.ceil(this.minWidth * height / this.minHeight);
36672 if(this.minWidth > this.minHeight){
36674 height = Math.ceil(this.minHeight * width / this.minWidth);
36677 this.thumbEl.setStyle({
36678 width : width + 'px',
36679 height : height + 'px'
36686 setThumbBoxPosition : function()
36688 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36689 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36691 this.thumbEl.setLeft(x);
36692 this.thumbEl.setTop(y);
36696 baseRotateLevel : function()
36698 this.baseRotate = 1;
36701 typeof(this.exif) != 'undefined' &&
36702 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36703 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36705 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36708 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36712 baseScaleLevel : function()
36716 if(this.isDocument){
36718 if(this.baseRotate == 6 || this.baseRotate == 8){
36720 height = this.thumbEl.getHeight();
36721 this.baseScale = height / this.imageEl.OriginWidth;
36723 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36724 width = this.thumbEl.getWidth();
36725 this.baseScale = width / this.imageEl.OriginHeight;
36731 height = this.thumbEl.getHeight();
36732 this.baseScale = height / this.imageEl.OriginHeight;
36734 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36735 width = this.thumbEl.getWidth();
36736 this.baseScale = width / this.imageEl.OriginWidth;
36742 if(this.baseRotate == 6 || this.baseRotate == 8){
36744 width = this.thumbEl.getHeight();
36745 this.baseScale = width / this.imageEl.OriginHeight;
36747 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36748 height = this.thumbEl.getWidth();
36749 this.baseScale = height / this.imageEl.OriginHeight;
36752 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36753 height = this.thumbEl.getWidth();
36754 this.baseScale = height / this.imageEl.OriginHeight;
36756 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36757 width = this.thumbEl.getHeight();
36758 this.baseScale = width / this.imageEl.OriginWidth;
36765 width = this.thumbEl.getWidth();
36766 this.baseScale = width / this.imageEl.OriginWidth;
36768 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36769 height = this.thumbEl.getHeight();
36770 this.baseScale = height / this.imageEl.OriginHeight;
36773 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36775 height = this.thumbEl.getHeight();
36776 this.baseScale = height / this.imageEl.OriginHeight;
36778 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36779 width = this.thumbEl.getWidth();
36780 this.baseScale = width / this.imageEl.OriginWidth;
36788 getScaleLevel : function()
36790 return this.baseScale * Math.pow(1.1, this.scale);
36793 onTouchStart : function(e)
36795 if(!this.canvasLoaded){
36796 this.beforeSelectFile(e);
36800 var touches = e.browserEvent.touches;
36806 if(touches.length == 1){
36807 this.onMouseDown(e);
36811 if(touches.length != 2){
36817 for(var i = 0, finger; finger = touches[i]; i++){
36818 coords.push(finger.pageX, finger.pageY);
36821 var x = Math.pow(coords[0] - coords[2], 2);
36822 var y = Math.pow(coords[1] - coords[3], 2);
36824 this.startDistance = Math.sqrt(x + y);
36826 this.startScale = this.scale;
36828 this.pinching = true;
36829 this.dragable = false;
36833 onTouchMove : function(e)
36835 if(!this.pinching && !this.dragable){
36839 var touches = e.browserEvent.touches;
36846 this.onMouseMove(e);
36852 for(var i = 0, finger; finger = touches[i]; i++){
36853 coords.push(finger.pageX, finger.pageY);
36856 var x = Math.pow(coords[0] - coords[2], 2);
36857 var y = Math.pow(coords[1] - coords[3], 2);
36859 this.endDistance = Math.sqrt(x + y);
36861 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36863 if(!this.zoomable()){
36864 this.scale = this.startScale;
36872 onTouchEnd : function(e)
36874 this.pinching = false;
36875 this.dragable = false;
36879 process : function(file, crop)
36882 this.maskEl.mask(this.loadingText);
36885 this.xhr = new XMLHttpRequest();
36887 file.xhr = this.xhr;
36889 this.xhr.open(this.method, this.url, true);
36892 "Accept": "application/json",
36893 "Cache-Control": "no-cache",
36894 "X-Requested-With": "XMLHttpRequest"
36897 for (var headerName in headers) {
36898 var headerValue = headers[headerName];
36900 this.xhr.setRequestHeader(headerName, headerValue);
36906 this.xhr.onload = function()
36908 _this.xhrOnLoad(_this.xhr);
36911 this.xhr.onerror = function()
36913 _this.xhrOnError(_this.xhr);
36916 var formData = new FormData();
36918 formData.append('returnHTML', 'NO');
36921 formData.append('crop', crop);
36924 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36925 formData.append(this.paramName, file, file.name);
36928 if(typeof(file.filename) != 'undefined'){
36929 formData.append('filename', file.filename);
36932 if(typeof(file.mimetype) != 'undefined'){
36933 formData.append('mimetype', file.mimetype);
36936 if(this.fireEvent('arrange', this, formData) != false){
36937 this.xhr.send(formData);
36941 xhrOnLoad : function(xhr)
36944 this.maskEl.unmask();
36947 if (xhr.readyState !== 4) {
36948 this.fireEvent('exception', this, xhr);
36952 var response = Roo.decode(xhr.responseText);
36954 if(!response.success){
36955 this.fireEvent('exception', this, xhr);
36959 var response = Roo.decode(xhr.responseText);
36961 this.fireEvent('upload', this, response);
36965 xhrOnError : function()
36968 this.maskEl.unmask();
36971 Roo.log('xhr on error');
36973 var response = Roo.decode(xhr.responseText);
36979 prepare : function(file)
36982 this.maskEl.mask(this.loadingText);
36988 if(typeof(file) === 'string'){
36989 this.loadCanvas(file);
36993 if(!file || !this.urlAPI){
36998 this.cropType = file.type;
37002 if(this.fireEvent('prepare', this, this.file) != false){
37004 var reader = new FileReader();
37006 reader.onload = function (e) {
37007 if (e.target.error) {
37008 Roo.log(e.target.error);
37012 var buffer = e.target.result,
37013 dataView = new DataView(buffer),
37015 maxOffset = dataView.byteLength - 4,
37019 if (dataView.getUint16(0) === 0xffd8) {
37020 while (offset < maxOffset) {
37021 markerBytes = dataView.getUint16(offset);
37023 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37024 markerLength = dataView.getUint16(offset + 2) + 2;
37025 if (offset + markerLength > dataView.byteLength) {
37026 Roo.log('Invalid meta data: Invalid segment size.');
37030 if(markerBytes == 0xffe1){
37031 _this.parseExifData(
37038 offset += markerLength;
37048 var url = _this.urlAPI.createObjectURL(_this.file);
37050 _this.loadCanvas(url);
37055 reader.readAsArrayBuffer(this.file);
37061 parseExifData : function(dataView, offset, length)
37063 var tiffOffset = offset + 10,
37067 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37068 // No Exif data, might be XMP data instead
37072 // Check for the ASCII code for "Exif" (0x45786966):
37073 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37074 // No Exif data, might be XMP data instead
37077 if (tiffOffset + 8 > dataView.byteLength) {
37078 Roo.log('Invalid Exif data: Invalid segment size.');
37081 // Check for the two null bytes:
37082 if (dataView.getUint16(offset + 8) !== 0x0000) {
37083 Roo.log('Invalid Exif data: Missing byte alignment offset.');
37086 // Check the byte alignment:
37087 switch (dataView.getUint16(tiffOffset)) {
37089 littleEndian = true;
37092 littleEndian = false;
37095 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37098 // Check for the TIFF tag marker (0x002A):
37099 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37100 Roo.log('Invalid Exif data: Missing TIFF marker.');
37103 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37104 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37106 this.parseExifTags(
37109 tiffOffset + dirOffset,
37114 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37119 if (dirOffset + 6 > dataView.byteLength) {
37120 Roo.log('Invalid Exif data: Invalid directory offset.');
37123 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37124 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37125 if (dirEndOffset + 4 > dataView.byteLength) {
37126 Roo.log('Invalid Exif data: Invalid directory size.');
37129 for (i = 0; i < tagsNumber; i += 1) {
37133 dirOffset + 2 + 12 * i, // tag offset
37137 // Return the offset to the next directory:
37138 return dataView.getUint32(dirEndOffset, littleEndian);
37141 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
37143 var tag = dataView.getUint16(offset, littleEndian);
37145 this.exif[tag] = this.getExifValue(
37149 dataView.getUint16(offset + 2, littleEndian), // tag type
37150 dataView.getUint32(offset + 4, littleEndian), // tag length
37155 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37157 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37166 Roo.log('Invalid Exif data: Invalid tag type.');
37170 tagSize = tagType.size * length;
37171 // Determine if the value is contained in the dataOffset bytes,
37172 // or if the value at the dataOffset is a pointer to the actual data:
37173 dataOffset = tagSize > 4 ?
37174 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37175 if (dataOffset + tagSize > dataView.byteLength) {
37176 Roo.log('Invalid Exif data: Invalid data offset.');
37179 if (length === 1) {
37180 return tagType.getValue(dataView, dataOffset, littleEndian);
37183 for (i = 0; i < length; i += 1) {
37184 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37187 if (tagType.ascii) {
37189 // Concatenate the chars:
37190 for (i = 0; i < values.length; i += 1) {
37192 // Ignore the terminating NULL byte(s):
37193 if (c === '\u0000') {
37205 Roo.apply(Roo.bootstrap.UploadCropbox, {
37207 'Orientation': 0x0112
37211 1: 0, //'top-left',
37213 3: 180, //'bottom-right',
37214 // 4: 'bottom-left',
37216 6: 90, //'right-top',
37217 // 7: 'right-bottom',
37218 8: 270 //'left-bottom'
37222 // byte, 8-bit unsigned int:
37224 getValue: function (dataView, dataOffset) {
37225 return dataView.getUint8(dataOffset);
37229 // ascii, 8-bit byte:
37231 getValue: function (dataView, dataOffset) {
37232 return String.fromCharCode(dataView.getUint8(dataOffset));
37237 // short, 16 bit int:
37239 getValue: function (dataView, dataOffset, littleEndian) {
37240 return dataView.getUint16(dataOffset, littleEndian);
37244 // long, 32 bit int:
37246 getValue: function (dataView, dataOffset, littleEndian) {
37247 return dataView.getUint32(dataOffset, littleEndian);
37251 // rational = two long values, first is numerator, second is denominator:
37253 getValue: function (dataView, dataOffset, littleEndian) {
37254 return dataView.getUint32(dataOffset, littleEndian) /
37255 dataView.getUint32(dataOffset + 4, littleEndian);
37259 // slong, 32 bit signed int:
37261 getValue: function (dataView, dataOffset, littleEndian) {
37262 return dataView.getInt32(dataOffset, littleEndian);
37266 // srational, two slongs, first is numerator, second is denominator:
37268 getValue: function (dataView, dataOffset, littleEndian) {
37269 return dataView.getInt32(dataOffset, littleEndian) /
37270 dataView.getInt32(dataOffset + 4, littleEndian);
37280 cls : 'btn-group roo-upload-cropbox-rotate-left',
37281 action : 'rotate-left',
37285 cls : 'btn btn-default',
37286 html : '<i class="fa fa-undo"></i>'
37292 cls : 'btn-group roo-upload-cropbox-picture',
37293 action : 'picture',
37297 cls : 'btn btn-default',
37298 html : '<i class="fa fa-picture-o"></i>'
37304 cls : 'btn-group roo-upload-cropbox-rotate-right',
37305 action : 'rotate-right',
37309 cls : 'btn btn-default',
37310 html : '<i class="fa fa-repeat"></i>'
37318 cls : 'btn-group roo-upload-cropbox-rotate-left',
37319 action : 'rotate-left',
37323 cls : 'btn btn-default',
37324 html : '<i class="fa fa-undo"></i>'
37330 cls : 'btn-group roo-upload-cropbox-download',
37331 action : 'download',
37335 cls : 'btn btn-default',
37336 html : '<i class="fa fa-download"></i>'
37342 cls : 'btn-group roo-upload-cropbox-crop',
37347 cls : 'btn btn-default',
37348 html : '<i class="fa fa-crop"></i>'
37354 cls : 'btn-group roo-upload-cropbox-trash',
37359 cls : 'btn btn-default',
37360 html : '<i class="fa fa-trash"></i>'
37366 cls : 'btn-group roo-upload-cropbox-rotate-right',
37367 action : 'rotate-right',
37371 cls : 'btn btn-default',
37372 html : '<i class="fa fa-repeat"></i>'
37380 cls : 'btn-group roo-upload-cropbox-rotate-left',
37381 action : 'rotate-left',
37385 cls : 'btn btn-default',
37386 html : '<i class="fa fa-undo"></i>'
37392 cls : 'btn-group roo-upload-cropbox-rotate-right',
37393 action : 'rotate-right',
37397 cls : 'btn btn-default',
37398 html : '<i class="fa fa-repeat"></i>'
37411 * @class Roo.bootstrap.DocumentManager
37412 * @extends Roo.bootstrap.Component
37413 * Bootstrap DocumentManager class
37414 * @cfg {String} paramName default 'imageUpload'
37415 * @cfg {String} toolTipName default 'filename'
37416 * @cfg {String} method default POST
37417 * @cfg {String} url action url
37418 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37419 * @cfg {Boolean} multiple multiple upload default true
37420 * @cfg {Number} thumbSize default 300
37421 * @cfg {String} fieldLabel
37422 * @cfg {Number} labelWidth default 4
37423 * @cfg {String} labelAlign (left|top) default left
37424 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37425 * @cfg {Number} labellg set the width of label (1-12)
37426 * @cfg {Number} labelmd set the width of label (1-12)
37427 * @cfg {Number} labelsm set the width of label (1-12)
37428 * @cfg {Number} labelxs set the width of label (1-12)
37431 * Create a new DocumentManager
37432 * @param {Object} config The config object
37435 Roo.bootstrap.DocumentManager = function(config){
37436 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37439 this.delegates = [];
37444 * Fire when initial the DocumentManager
37445 * @param {Roo.bootstrap.DocumentManager} this
37450 * inspect selected file
37451 * @param {Roo.bootstrap.DocumentManager} this
37452 * @param {File} file
37457 * Fire when xhr load exception
37458 * @param {Roo.bootstrap.DocumentManager} this
37459 * @param {XMLHttpRequest} xhr
37461 "exception" : true,
37463 * @event afterupload
37464 * Fire when xhr load exception
37465 * @param {Roo.bootstrap.DocumentManager} this
37466 * @param {XMLHttpRequest} xhr
37468 "afterupload" : true,
37471 * prepare the form data
37472 * @param {Roo.bootstrap.DocumentManager} this
37473 * @param {Object} formData
37478 * Fire when remove the file
37479 * @param {Roo.bootstrap.DocumentManager} this
37480 * @param {Object} file
37485 * Fire after refresh the file
37486 * @param {Roo.bootstrap.DocumentManager} this
37491 * Fire after click the image
37492 * @param {Roo.bootstrap.DocumentManager} this
37493 * @param {Object} file
37498 * Fire when upload a image and editable set to true
37499 * @param {Roo.bootstrap.DocumentManager} this
37500 * @param {Object} file
37504 * @event beforeselectfile
37505 * Fire before select file
37506 * @param {Roo.bootstrap.DocumentManager} this
37508 "beforeselectfile" : true,
37511 * Fire before process file
37512 * @param {Roo.bootstrap.DocumentManager} this
37513 * @param {Object} file
37517 * @event previewrendered
37518 * Fire when preview rendered
37519 * @param {Roo.bootstrap.DocumentManager} this
37520 * @param {Object} file
37522 "previewrendered" : true,
37525 "previewResize" : true
37530 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
37539 paramName : 'imageUpload',
37540 toolTipName : 'filename',
37543 labelAlign : 'left',
37553 getAutoCreate : function()
37555 var managerWidget = {
37557 cls : 'roo-document-manager',
37561 cls : 'roo-document-manager-selector',
37566 cls : 'roo-document-manager-uploader',
37570 cls : 'roo-document-manager-upload-btn',
37571 html : '<i class="fa fa-plus"></i>'
37582 cls : 'column col-md-12',
37587 if(this.fieldLabel.length){
37592 cls : 'column col-md-12',
37593 html : this.fieldLabel
37597 cls : 'column col-md-12',
37602 if(this.labelAlign == 'left'){
37607 html : this.fieldLabel
37616 if(this.labelWidth > 12){
37617 content[0].style = "width: " + this.labelWidth + 'px';
37620 if(this.labelWidth < 13 && this.labelmd == 0){
37621 this.labelmd = this.labelWidth;
37624 if(this.labellg > 0){
37625 content[0].cls += ' col-lg-' + this.labellg;
37626 content[1].cls += ' col-lg-' + (12 - this.labellg);
37629 if(this.labelmd > 0){
37630 content[0].cls += ' col-md-' + this.labelmd;
37631 content[1].cls += ' col-md-' + (12 - this.labelmd);
37634 if(this.labelsm > 0){
37635 content[0].cls += ' col-sm-' + this.labelsm;
37636 content[1].cls += ' col-sm-' + (12 - this.labelsm);
37639 if(this.labelxs > 0){
37640 content[0].cls += ' col-xs-' + this.labelxs;
37641 content[1].cls += ' col-xs-' + (12 - this.labelxs);
37649 cls : 'row clearfix',
37657 initEvents : function()
37659 this.managerEl = this.el.select('.roo-document-manager', true).first();
37660 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37662 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37663 this.selectorEl.hide();
37666 this.selectorEl.attr('multiple', 'multiple');
37669 this.selectorEl.on('change', this.onFileSelected, this);
37671 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37672 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37674 this.uploader.on('click', this.onUploaderClick, this);
37676 this.renderProgressDialog();
37680 window.addEventListener("resize", function() { _this.refresh(); } );
37682 this.fireEvent('initial', this);
37685 renderProgressDialog : function()
37689 this.progressDialog = new Roo.bootstrap.Modal({
37690 cls : 'roo-document-manager-progress-dialog',
37691 allow_close : false,
37702 btnclick : function() {
37703 _this.uploadCancel();
37709 this.progressDialog.render(Roo.get(document.body));
37711 this.progress = new Roo.bootstrap.Progress({
37712 cls : 'roo-document-manager-progress',
37717 this.progress.render(this.progressDialog.getChildContainer());
37719 this.progressBar = new Roo.bootstrap.ProgressBar({
37720 cls : 'roo-document-manager-progress-bar',
37723 aria_valuemax : 12,
37727 this.progressBar.render(this.progress.getChildContainer());
37730 onUploaderClick : function(e)
37732 e.preventDefault();
37734 if(this.fireEvent('beforeselectfile', this) != false){
37735 this.selectorEl.dom.click();
37740 onFileSelected : function(e)
37742 e.preventDefault();
37744 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37748 Roo.each(this.selectorEl.dom.files, function(file){
37749 if(this.fireEvent('inspect', this, file) != false){
37750 this.files.push(file);
37760 this.selectorEl.dom.value = '';
37762 if(!this.files || !this.files.length){
37766 if(this.boxes > 0 && this.files.length > this.boxes){
37767 this.files = this.files.slice(0, this.boxes);
37770 this.uploader.show();
37772 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37773 this.uploader.hide();
37782 Roo.each(this.files, function(file){
37784 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37785 var f = this.renderPreview(file);
37790 if(file.type.indexOf('image') != -1){
37791 this.delegates.push(
37793 _this.process(file);
37794 }).createDelegate(this)
37802 _this.process(file);
37803 }).createDelegate(this)
37808 this.files = files;
37810 this.delegates = this.delegates.concat(docs);
37812 if(!this.delegates.length){
37817 this.progressBar.aria_valuemax = this.delegates.length;
37824 arrange : function()
37826 if(!this.delegates.length){
37827 this.progressDialog.hide();
37832 var delegate = this.delegates.shift();
37834 this.progressDialog.show();
37836 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37838 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37843 refresh : function()
37845 this.uploader.show();
37847 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37848 this.uploader.hide();
37851 Roo.isTouch ? this.closable(false) : this.closable(true);
37853 this.fireEvent('refresh', this);
37856 onRemove : function(e, el, o)
37858 e.preventDefault();
37860 this.fireEvent('remove', this, o);
37864 remove : function(o)
37868 Roo.each(this.files, function(file){
37869 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37878 this.files = files;
37885 Roo.each(this.files, function(file){
37890 file.target.remove();
37899 onClick : function(e, el, o)
37901 e.preventDefault();
37903 this.fireEvent('click', this, o);
37907 closable : function(closable)
37909 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37911 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37923 xhrOnLoad : function(xhr)
37925 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37929 if (xhr.readyState !== 4) {
37931 this.fireEvent('exception', this, xhr);
37935 var response = Roo.decode(xhr.responseText);
37937 if(!response.success){
37939 this.fireEvent('exception', this, xhr);
37943 var file = this.renderPreview(response.data);
37945 this.files.push(file);
37949 this.fireEvent('afterupload', this, xhr);
37953 xhrOnError : function(xhr)
37955 Roo.log('xhr on error');
37957 var response = Roo.decode(xhr.responseText);
37964 process : function(file)
37966 if(this.fireEvent('process', this, file) !== false){
37967 if(this.editable && file.type.indexOf('image') != -1){
37968 this.fireEvent('edit', this, file);
37972 this.uploadStart(file, false);
37979 uploadStart : function(file, crop)
37981 this.xhr = new XMLHttpRequest();
37983 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37988 file.xhr = this.xhr;
37990 this.managerEl.createChild({
37992 cls : 'roo-document-manager-loading',
37996 tooltip : file.name,
37997 cls : 'roo-document-manager-thumb',
37998 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38004 this.xhr.open(this.method, this.url, true);
38007 "Accept": "application/json",
38008 "Cache-Control": "no-cache",
38009 "X-Requested-With": "XMLHttpRequest"
38012 for (var headerName in headers) {
38013 var headerValue = headers[headerName];
38015 this.xhr.setRequestHeader(headerName, headerValue);
38021 this.xhr.onload = function()
38023 _this.xhrOnLoad(_this.xhr);
38026 this.xhr.onerror = function()
38028 _this.xhrOnError(_this.xhr);
38031 var formData = new FormData();
38033 formData.append('returnHTML', 'NO');
38036 formData.append('crop', crop);
38039 formData.append(this.paramName, file, file.name);
38046 if(this.fireEvent('prepare', this, formData, options) != false){
38048 if(options.manually){
38052 this.xhr.send(formData);
38056 this.uploadCancel();
38059 uploadCancel : function()
38065 this.delegates = [];
38067 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38074 renderPreview : function(file)
38076 if(typeof(file.target) != 'undefined' && file.target){
38080 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38082 var previewEl = this.managerEl.createChild({
38084 cls : 'roo-document-manager-preview',
38088 tooltip : file[this.toolTipName],
38089 cls : 'roo-document-manager-thumb',
38090 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38095 html : '<i class="fa fa-times-circle"></i>'
38100 var close = previewEl.select('button.close', true).first();
38102 close.on('click', this.onRemove, this, file);
38104 file.target = previewEl;
38106 var image = previewEl.select('img', true).first();
38110 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38112 image.on('click', this.onClick, this, file);
38114 this.fireEvent('previewrendered', this, file);
38120 onPreviewLoad : function(file, image)
38122 if(typeof(file.target) == 'undefined' || !file.target){
38126 var width = image.dom.naturalWidth || image.dom.width;
38127 var height = image.dom.naturalHeight || image.dom.height;
38129 if(!this.previewResize) {
38133 if(width > height){
38134 file.target.addClass('wide');
38138 file.target.addClass('tall');
38143 uploadFromSource : function(file, crop)
38145 this.xhr = new XMLHttpRequest();
38147 this.managerEl.createChild({
38149 cls : 'roo-document-manager-loading',
38153 tooltip : file.name,
38154 cls : 'roo-document-manager-thumb',
38155 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38161 this.xhr.open(this.method, this.url, true);
38164 "Accept": "application/json",
38165 "Cache-Control": "no-cache",
38166 "X-Requested-With": "XMLHttpRequest"
38169 for (var headerName in headers) {
38170 var headerValue = headers[headerName];
38172 this.xhr.setRequestHeader(headerName, headerValue);
38178 this.xhr.onload = function()
38180 _this.xhrOnLoad(_this.xhr);
38183 this.xhr.onerror = function()
38185 _this.xhrOnError(_this.xhr);
38188 var formData = new FormData();
38190 formData.append('returnHTML', 'NO');
38192 formData.append('crop', crop);
38194 if(typeof(file.filename) != 'undefined'){
38195 formData.append('filename', file.filename);
38198 if(typeof(file.mimetype) != 'undefined'){
38199 formData.append('mimetype', file.mimetype);
38204 if(this.fireEvent('prepare', this, formData) != false){
38205 this.xhr.send(formData);
38215 * @class Roo.bootstrap.DocumentViewer
38216 * @extends Roo.bootstrap.Component
38217 * Bootstrap DocumentViewer class
38218 * @cfg {Boolean} showDownload (true|false) show download button (default true)
38219 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38222 * Create a new DocumentViewer
38223 * @param {Object} config The config object
38226 Roo.bootstrap.DocumentViewer = function(config){
38227 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38232 * Fire after initEvent
38233 * @param {Roo.bootstrap.DocumentViewer} this
38239 * @param {Roo.bootstrap.DocumentViewer} this
38244 * Fire after download button
38245 * @param {Roo.bootstrap.DocumentViewer} this
38250 * Fire after trash button
38251 * @param {Roo.bootstrap.DocumentViewer} this
38258 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
38260 showDownload : true,
38264 getAutoCreate : function()
38268 cls : 'roo-document-viewer',
38272 cls : 'roo-document-viewer-body',
38276 cls : 'roo-document-viewer-thumb',
38280 cls : 'roo-document-viewer-image'
38288 cls : 'roo-document-viewer-footer',
38291 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38295 cls : 'btn-group roo-document-viewer-download',
38299 cls : 'btn btn-default',
38300 html : '<i class="fa fa-download"></i>'
38306 cls : 'btn-group roo-document-viewer-trash',
38310 cls : 'btn btn-default',
38311 html : '<i class="fa fa-trash"></i>'
38324 initEvents : function()
38326 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38327 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38329 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38330 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38332 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38333 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38335 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38336 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38338 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38339 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38341 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38342 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38344 this.bodyEl.on('click', this.onClick, this);
38345 this.downloadBtn.on('click', this.onDownload, this);
38346 this.trashBtn.on('click', this.onTrash, this);
38348 this.downloadBtn.hide();
38349 this.trashBtn.hide();
38351 if(this.showDownload){
38352 this.downloadBtn.show();
38355 if(this.showTrash){
38356 this.trashBtn.show();
38359 if(!this.showDownload && !this.showTrash) {
38360 this.footerEl.hide();
38365 initial : function()
38367 this.fireEvent('initial', this);
38371 onClick : function(e)
38373 e.preventDefault();
38375 this.fireEvent('click', this);
38378 onDownload : function(e)
38380 e.preventDefault();
38382 this.fireEvent('download', this);
38385 onTrash : function(e)
38387 e.preventDefault();
38389 this.fireEvent('trash', this);
38401 * @class Roo.bootstrap.form.FieldLabel
38402 * @extends Roo.bootstrap.Component
38403 * Bootstrap FieldLabel class
38404 * @cfg {String} html contents of the element
38405 * @cfg {String} tag tag of the element default label
38406 * @cfg {String} cls class of the element
38407 * @cfg {String} target label target
38408 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38409 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38410 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38411 * @cfg {String} iconTooltip default "This field is required"
38412 * @cfg {String} indicatorpos (left|right) default left
38415 * Create a new FieldLabel
38416 * @param {Object} config The config object
38419 Roo.bootstrap.form.FieldLabel = function(config){
38420 Roo.bootstrap.Element.superclass.constructor.call(this, config);
38425 * Fires after the field has been marked as invalid.
38426 * @param {Roo.form.FieldLabel} this
38427 * @param {String} msg The validation message
38432 * Fires after the field has been validated with no errors.
38433 * @param {Roo.form.FieldLabel} this
38439 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
38446 invalidClass : 'has-warning',
38447 validClass : 'has-success',
38448 iconTooltip : 'This field is required',
38449 indicatorpos : 'left',
38451 getAutoCreate : function(){
38454 if (!this.allowBlank) {
38460 cls : 'roo-bootstrap-field-label ' + this.cls,
38465 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38466 tooltip : this.iconTooltip
38475 if(this.indicatorpos == 'right'){
38478 cls : 'roo-bootstrap-field-label ' + this.cls,
38487 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38488 tooltip : this.iconTooltip
38497 initEvents: function()
38499 Roo.bootstrap.Element.superclass.initEvents.call(this);
38501 this.indicator = this.indicatorEl();
38503 if(this.indicator){
38504 this.indicator.removeClass('visible');
38505 this.indicator.addClass('invisible');
38508 Roo.bootstrap.form.FieldLabel.register(this);
38511 indicatorEl : function()
38513 var indicator = this.el.select('i.roo-required-indicator',true).first();
38524 * Mark this field as valid
38526 markValid : function()
38528 if(this.indicator){
38529 this.indicator.removeClass('visible');
38530 this.indicator.addClass('invisible');
38532 if (Roo.bootstrap.version == 3) {
38533 this.el.removeClass(this.invalidClass);
38534 this.el.addClass(this.validClass);
38536 this.el.removeClass('is-invalid');
38537 this.el.addClass('is-valid');
38541 this.fireEvent('valid', this);
38545 * Mark this field as invalid
38546 * @param {String} msg The validation message
38548 markInvalid : function(msg)
38550 if(this.indicator){
38551 this.indicator.removeClass('invisible');
38552 this.indicator.addClass('visible');
38554 if (Roo.bootstrap.version == 3) {
38555 this.el.removeClass(this.validClass);
38556 this.el.addClass(this.invalidClass);
38558 this.el.removeClass('is-valid');
38559 this.el.addClass('is-invalid');
38563 this.fireEvent('invalid', this, msg);
38569 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38574 * register a FieldLabel Group
38575 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38577 register : function(label)
38579 if(this.groups.hasOwnProperty(label.target)){
38583 this.groups[label.target] = label;
38587 * fetch a FieldLabel Group based on the target
38588 * @param {string} target
38589 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38591 get: function(target) {
38592 if (typeof(this.groups[target]) == 'undefined') {
38596 return this.groups[target] ;
38605 * page DateSplitField.
38611 * @class Roo.bootstrap.form.DateSplitField
38612 * @extends Roo.bootstrap.Component
38613 * Bootstrap DateSplitField class
38614 * @cfg {string} fieldLabel - the label associated
38615 * @cfg {Number} labelWidth set the width of label (0-12)
38616 * @cfg {String} labelAlign (top|left)
38617 * @cfg {Boolean} dayAllowBlank (true|false) default false
38618 * @cfg {Boolean} monthAllowBlank (true|false) default false
38619 * @cfg {Boolean} yearAllowBlank (true|false) default false
38620 * @cfg {string} dayPlaceholder
38621 * @cfg {string} monthPlaceholder
38622 * @cfg {string} yearPlaceholder
38623 * @cfg {string} dayFormat default 'd'
38624 * @cfg {string} monthFormat default 'm'
38625 * @cfg {string} yearFormat default 'Y'
38626 * @cfg {Number} labellg set the width of label (1-12)
38627 * @cfg {Number} labelmd set the width of label (1-12)
38628 * @cfg {Number} labelsm set the width of label (1-12)
38629 * @cfg {Number} labelxs set the width of label (1-12)
38633 * Create a new DateSplitField
38634 * @param {Object} config The config object
38637 Roo.bootstrap.form.DateSplitField = function(config){
38638 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38644 * getting the data of years
38645 * @param {Roo.bootstrap.form.DateSplitField} this
38646 * @param {Object} years
38651 * getting the data of days
38652 * @param {Roo.bootstrap.form.DateSplitField} this
38653 * @param {Object} days
38658 * Fires after the field has been marked as invalid.
38659 * @param {Roo.form.Field} this
38660 * @param {String} msg The validation message
38665 * Fires after the field has been validated with no errors.
38666 * @param {Roo.form.Field} this
38672 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
38675 labelAlign : 'top',
38677 dayAllowBlank : false,
38678 monthAllowBlank : false,
38679 yearAllowBlank : false,
38680 dayPlaceholder : '',
38681 monthPlaceholder : '',
38682 yearPlaceholder : '',
38686 isFormField : true,
38692 getAutoCreate : function()
38696 cls : 'row roo-date-split-field-group',
38701 cls : 'form-hidden-field roo-date-split-field-group-value',
38707 var labelCls = 'col-md-12';
38708 var contentCls = 'col-md-4';
38710 if(this.fieldLabel){
38714 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38718 html : this.fieldLabel
38723 if(this.labelAlign == 'left'){
38725 if(this.labelWidth > 12){
38726 label.style = "width: " + this.labelWidth + 'px';
38729 if(this.labelWidth < 13 && this.labelmd == 0){
38730 this.labelmd = this.labelWidth;
38733 if(this.labellg > 0){
38734 labelCls = ' col-lg-' + this.labellg;
38735 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38738 if(this.labelmd > 0){
38739 labelCls = ' col-md-' + this.labelmd;
38740 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38743 if(this.labelsm > 0){
38744 labelCls = ' col-sm-' + this.labelsm;
38745 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38748 if(this.labelxs > 0){
38749 labelCls = ' col-xs-' + this.labelxs;
38750 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38754 label.cls += ' ' + labelCls;
38756 cfg.cn.push(label);
38759 Roo.each(['day', 'month', 'year'], function(t){
38762 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38769 inputEl: function ()
38771 return this.el.select('.roo-date-split-field-group-value', true).first();
38774 onRender : function(ct, position)
38778 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38780 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38782 this.dayField = new Roo.bootstrap.form.ComboBox({
38783 allowBlank : this.dayAllowBlank,
38784 alwaysQuery : true,
38785 displayField : 'value',
38788 forceSelection : true,
38790 placeholder : this.dayPlaceholder,
38791 selectOnFocus : true,
38792 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38793 triggerAction : 'all',
38795 valueField : 'value',
38796 store : new Roo.data.SimpleStore({
38797 data : (function() {
38799 _this.fireEvent('days', _this, days);
38802 fields : [ 'value' ]
38805 select : function (_self, record, index)
38807 _this.setValue(_this.getValue());
38812 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38814 this.monthField = new Roo.bootstrap.form.MonthField({
38815 after : '<i class=\"fa fa-calendar\"></i>',
38816 allowBlank : this.monthAllowBlank,
38817 placeholder : this.monthPlaceholder,
38820 render : function (_self)
38822 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38823 e.preventDefault();
38827 select : function (_self, oldvalue, newvalue)
38829 _this.setValue(_this.getValue());
38834 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38836 this.yearField = new Roo.bootstrap.form.ComboBox({
38837 allowBlank : this.yearAllowBlank,
38838 alwaysQuery : true,
38839 displayField : 'value',
38842 forceSelection : true,
38844 placeholder : this.yearPlaceholder,
38845 selectOnFocus : true,
38846 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38847 triggerAction : 'all',
38849 valueField : 'value',
38850 store : new Roo.data.SimpleStore({
38851 data : (function() {
38853 _this.fireEvent('years', _this, years);
38856 fields : [ 'value' ]
38859 select : function (_self, record, index)
38861 _this.setValue(_this.getValue());
38866 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38869 setValue : function(v, format)
38871 this.inputEl.dom.value = v;
38873 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38875 var d = Date.parseDate(v, f);
38882 this.setDay(d.format(this.dayFormat));
38883 this.setMonth(d.format(this.monthFormat));
38884 this.setYear(d.format(this.yearFormat));
38891 setDay : function(v)
38893 this.dayField.setValue(v);
38894 this.inputEl.dom.value = this.getValue();
38899 setMonth : function(v)
38901 this.monthField.setValue(v, true);
38902 this.inputEl.dom.value = this.getValue();
38907 setYear : function(v)
38909 this.yearField.setValue(v);
38910 this.inputEl.dom.value = this.getValue();
38915 getDay : function()
38917 return this.dayField.getValue();
38920 getMonth : function()
38922 return this.monthField.getValue();
38925 getYear : function()
38927 return this.yearField.getValue();
38930 getValue : function()
38932 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38934 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38944 this.inputEl.dom.value = '';
38949 validate : function()
38951 var d = this.dayField.validate();
38952 var m = this.monthField.validate();
38953 var y = this.yearField.validate();
38958 (!this.dayAllowBlank && !d) ||
38959 (!this.monthAllowBlank && !m) ||
38960 (!this.yearAllowBlank && !y)
38965 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38974 this.markInvalid();
38979 markValid : function()
38982 var label = this.el.select('label', true).first();
38983 var icon = this.el.select('i.fa-star', true).first();
38989 this.fireEvent('valid', this);
38993 * Mark this field as invalid
38994 * @param {String} msg The validation message
38996 markInvalid : function(msg)
38999 var label = this.el.select('label', true).first();
39000 var icon = this.el.select('i.fa-star', true).first();
39002 if(label && !icon){
39003 this.el.select('.roo-date-split-field-label', true).createChild({
39005 cls : 'text-danger fa fa-lg fa-star',
39006 tooltip : 'This field is required',
39007 style : 'margin-right:5px;'
39011 this.fireEvent('invalid', this, msg);
39014 clearInvalid : function()
39016 var label = this.el.select('label', true).first();
39017 var icon = this.el.select('i.fa-star', true).first();
39023 this.fireEvent('valid', this);
39026 getName: function()
39036 * @class Roo.bootstrap.LayoutMasonry
39037 * @extends Roo.bootstrap.Component
39038 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39039 * Bootstrap Layout Masonry class
39042 * http://masonry.desandro.com
39044 * The idea is to render all the bricks based on vertical width...
39046 * The original code extends 'outlayer' - we might need to use that....
39049 * Create a new Element
39050 * @param {Object} config The config object
39053 Roo.bootstrap.LayoutMasonry = function(config){
39055 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39059 Roo.bootstrap.LayoutMasonry.register(this);
39065 * Fire after layout the items
39066 * @param {Roo.bootstrap.LayoutMasonry} this
39067 * @param {Roo.EventObject} e
39074 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
39077 * @cfg {Boolean} isLayoutInstant = no animation?
39079 isLayoutInstant : false, // needed?
39082 * @cfg {Number} boxWidth width of the columns
39087 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
39092 * @cfg {Number} padWidth padding below box..
39097 * @cfg {Number} gutter gutter width..
39102 * @cfg {Number} maxCols maximum number of columns
39108 * @cfg {Boolean} isAutoInitial defalut true
39110 isAutoInitial : true,
39115 * @cfg {Boolean} isHorizontal defalut false
39117 isHorizontal : false,
39119 currentSize : null,
39125 bricks: null, //CompositeElement
39129 _isLayoutInited : false,
39131 // isAlternative : false, // only use for vertical layout...
39134 * @cfg {Number} alternativePadWidth padding below box..
39136 alternativePadWidth : 50,
39138 selectedBrick : [],
39140 getAutoCreate : function(){
39142 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39146 cls: 'blog-masonary-wrapper ' + this.cls,
39148 cls : 'mas-boxes masonary'
39155 getChildContainer: function( )
39157 if (this.boxesEl) {
39158 return this.boxesEl;
39161 this.boxesEl = this.el.select('.mas-boxes').first();
39163 return this.boxesEl;
39167 initEvents : function()
39171 if(this.isAutoInitial){
39172 Roo.log('hook children rendered');
39173 this.on('childrenrendered', function() {
39174 Roo.log('children rendered');
39180 initial : function()
39182 this.selectedBrick = [];
39184 this.currentSize = this.el.getBox(true);
39186 Roo.EventManager.onWindowResize(this.resize, this);
39188 if(!this.isAutoInitial){
39196 //this.layout.defer(500,this);
39200 resize : function()
39202 var cs = this.el.getBox(true);
39205 this.currentSize.width == cs.width &&
39206 this.currentSize.x == cs.x &&
39207 this.currentSize.height == cs.height &&
39208 this.currentSize.y == cs.y
39210 Roo.log("no change in with or X or Y");
39214 this.currentSize = cs;
39220 layout : function()
39222 this._resetLayout();
39224 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39226 this.layoutItems( isInstant );
39228 this._isLayoutInited = true;
39230 this.fireEvent('layout', this);
39234 _resetLayout : function()
39236 if(this.isHorizontal){
39237 this.horizontalMeasureColumns();
39241 this.verticalMeasureColumns();
39245 verticalMeasureColumns : function()
39247 this.getContainerWidth();
39249 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39250 // this.colWidth = Math.floor(this.containerWidth * 0.8);
39254 var boxWidth = this.boxWidth + this.padWidth;
39256 if(this.containerWidth < this.boxWidth){
39257 boxWidth = this.containerWidth
39260 var containerWidth = this.containerWidth;
39262 var cols = Math.floor(containerWidth / boxWidth);
39264 this.cols = Math.max( cols, 1 );
39266 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39268 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39270 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39272 this.colWidth = boxWidth + avail - this.padWidth;
39274 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39275 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
39278 horizontalMeasureColumns : function()
39280 this.getContainerWidth();
39282 var boxWidth = this.boxWidth;
39284 if(this.containerWidth < boxWidth){
39285 boxWidth = this.containerWidth;
39288 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39290 this.el.setHeight(boxWidth);
39294 getContainerWidth : function()
39296 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
39299 layoutItems : function( isInstant )
39301 Roo.log(this.bricks);
39303 var items = Roo.apply([], this.bricks);
39305 if(this.isHorizontal){
39306 this._horizontalLayoutItems( items , isInstant );
39310 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39311 // this._verticalAlternativeLayoutItems( items , isInstant );
39315 this._verticalLayoutItems( items , isInstant );
39319 _verticalLayoutItems : function ( items , isInstant)
39321 if ( !items || !items.length ) {
39326 ['xs', 'xs', 'xs', 'tall'],
39327 ['xs', 'xs', 'tall'],
39328 ['xs', 'xs', 'sm'],
39329 ['xs', 'xs', 'xs'],
39335 ['sm', 'xs', 'xs'],
39339 ['tall', 'xs', 'xs', 'xs'],
39340 ['tall', 'xs', 'xs'],
39352 Roo.each(items, function(item, k){
39354 switch (item.size) {
39355 // these layouts take up a full box,
39366 boxes.push([item]);
39389 var filterPattern = function(box, length)
39397 var pattern = box.slice(0, length);
39401 Roo.each(pattern, function(i){
39402 format.push(i.size);
39405 Roo.each(standard, function(s){
39407 if(String(s) != String(format)){
39416 if(!match && length == 1){
39421 filterPattern(box, length - 1);
39425 queue.push(pattern);
39427 box = box.slice(length, box.length);
39429 filterPattern(box, 4);
39435 Roo.each(boxes, function(box, k){
39441 if(box.length == 1){
39446 filterPattern(box, 4);
39450 this._processVerticalLayoutQueue( queue, isInstant );
39454 // _verticalAlternativeLayoutItems : function( items , isInstant )
39456 // if ( !items || !items.length ) {
39460 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
39464 _horizontalLayoutItems : function ( items , isInstant)
39466 if ( !items || !items.length || items.length < 3) {
39472 var eItems = items.slice(0, 3);
39474 items = items.slice(3, items.length);
39477 ['xs', 'xs', 'xs', 'wide'],
39478 ['xs', 'xs', 'wide'],
39479 ['xs', 'xs', 'sm'],
39480 ['xs', 'xs', 'xs'],
39486 ['sm', 'xs', 'xs'],
39490 ['wide', 'xs', 'xs', 'xs'],
39491 ['wide', 'xs', 'xs'],
39504 Roo.each(items, function(item, k){
39506 switch (item.size) {
39517 boxes.push([item]);
39541 var filterPattern = function(box, length)
39549 var pattern = box.slice(0, length);
39553 Roo.each(pattern, function(i){
39554 format.push(i.size);
39557 Roo.each(standard, function(s){
39559 if(String(s) != String(format)){
39568 if(!match && length == 1){
39573 filterPattern(box, length - 1);
39577 queue.push(pattern);
39579 box = box.slice(length, box.length);
39581 filterPattern(box, 4);
39587 Roo.each(boxes, function(box, k){
39593 if(box.length == 1){
39598 filterPattern(box, 4);
39605 var pos = this.el.getBox(true);
39609 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39611 var hit_end = false;
39613 Roo.each(queue, function(box){
39617 Roo.each(box, function(b){
39619 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39629 Roo.each(box, function(b){
39631 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39634 mx = Math.max(mx, b.x);
39638 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39642 Roo.each(box, function(b){
39644 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39658 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39661 /** Sets position of item in DOM
39662 * @param {Element} item
39663 * @param {Number} x - horizontal position
39664 * @param {Number} y - vertical position
39665 * @param {Boolean} isInstant - disables transitions
39667 _processVerticalLayoutQueue : function( queue, isInstant )
39669 var pos = this.el.getBox(true);
39674 for (var i = 0; i < this.cols; i++){
39678 Roo.each(queue, function(box, k){
39680 var col = k % this.cols;
39682 Roo.each(box, function(b,kk){
39684 b.el.position('absolute');
39686 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39687 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39689 if(b.size == 'md-left' || b.size == 'md-right'){
39690 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39691 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39694 b.el.setWidth(width);
39695 b.el.setHeight(height);
39697 b.el.select('iframe',true).setSize(width,height);
39701 for (var i = 0; i < this.cols; i++){
39703 if(maxY[i] < maxY[col]){
39708 col = Math.min(col, i);
39712 x = pos.x + col * (this.colWidth + this.padWidth);
39716 var positions = [];
39718 switch (box.length){
39720 positions = this.getVerticalOneBoxColPositions(x, y, box);
39723 positions = this.getVerticalTwoBoxColPositions(x, y, box);
39726 positions = this.getVerticalThreeBoxColPositions(x, y, box);
39729 positions = this.getVerticalFourBoxColPositions(x, y, box);
39735 Roo.each(box, function(b,kk){
39737 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39739 var sz = b.el.getSize();
39741 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39749 for (var i = 0; i < this.cols; i++){
39750 mY = Math.max(mY, maxY[i]);
39753 this.el.setHeight(mY - pos.y);
39757 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39759 // var pos = this.el.getBox(true);
39762 // var maxX = pos.right;
39764 // var maxHeight = 0;
39766 // Roo.each(items, function(item, k){
39770 // item.el.position('absolute');
39772 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39774 // item.el.setWidth(width);
39776 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39778 // item.el.setHeight(height);
39781 // item.el.setXY([x, y], isInstant ? false : true);
39783 // item.el.setXY([maxX - width, y], isInstant ? false : true);
39786 // y = y + height + this.alternativePadWidth;
39788 // maxHeight = maxHeight + height + this.alternativePadWidth;
39792 // this.el.setHeight(maxHeight);
39796 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39798 var pos = this.el.getBox(true);
39803 var maxX = pos.right;
39805 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39807 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39809 Roo.each(queue, function(box, k){
39811 Roo.each(box, function(b, kk){
39813 b.el.position('absolute');
39815 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39816 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39818 if(b.size == 'md-left' || b.size == 'md-right'){
39819 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39820 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39823 b.el.setWidth(width);
39824 b.el.setHeight(height);
39832 var positions = [];
39834 switch (box.length){
39836 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39839 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39842 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39845 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39851 Roo.each(box, function(b,kk){
39853 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39855 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39863 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39865 Roo.each(eItems, function(b,k){
39867 b.size = (k == 0) ? 'sm' : 'xs';
39868 b.x = (k == 0) ? 2 : 1;
39869 b.y = (k == 0) ? 2 : 1;
39871 b.el.position('absolute');
39873 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39875 b.el.setWidth(width);
39877 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39879 b.el.setHeight(height);
39883 var positions = [];
39886 x : maxX - this.unitWidth * 2 - this.gutter,
39891 x : maxX - this.unitWidth,
39892 y : minY + (this.unitWidth + this.gutter) * 2
39896 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39900 Roo.each(eItems, function(b,k){
39902 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39908 getVerticalOneBoxColPositions : function(x, y, box)
39912 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39914 if(box[0].size == 'md-left'){
39918 if(box[0].size == 'md-right'){
39923 x : x + (this.unitWidth + this.gutter) * rand,
39930 getVerticalTwoBoxColPositions : function(x, y, box)
39934 if(box[0].size == 'xs'){
39938 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39942 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39956 x : x + (this.unitWidth + this.gutter) * 2,
39957 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39964 getVerticalThreeBoxColPositions : function(x, y, box)
39968 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39976 x : x + (this.unitWidth + this.gutter) * 1,
39981 x : x + (this.unitWidth + this.gutter) * 2,
39989 if(box[0].size == 'xs' && box[1].size == 'xs'){
39998 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
40002 x : x + (this.unitWidth + this.gutter) * 1,
40016 x : x + (this.unitWidth + this.gutter) * 2,
40021 x : x + (this.unitWidth + this.gutter) * 2,
40022 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40029 getVerticalFourBoxColPositions : function(x, y, box)
40033 if(box[0].size == 'xs'){
40042 y : y + (this.unitHeight + this.gutter) * 1
40047 y : y + (this.unitHeight + this.gutter) * 2
40051 x : x + (this.unitWidth + this.gutter) * 1,
40065 x : x + (this.unitWidth + this.gutter) * 2,
40070 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40071 y : y + (this.unitHeight + this.gutter) * 1
40075 x : x + (this.unitWidth + this.gutter) * 2,
40076 y : y + (this.unitWidth + this.gutter) * 2
40083 getHorizontalOneBoxColPositions : function(maxX, minY, box)
40087 if(box[0].size == 'md-left'){
40089 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40096 if(box[0].size == 'md-right'){
40098 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40099 y : minY + (this.unitWidth + this.gutter) * 1
40105 var rand = Math.floor(Math.random() * (4 - box[0].y));
40108 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40109 y : minY + (this.unitWidth + this.gutter) * rand
40116 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40120 if(box[0].size == 'xs'){
40123 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40128 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40129 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40137 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40142 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40143 y : minY + (this.unitWidth + this.gutter) * 2
40150 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40154 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40157 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40162 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40163 y : minY + (this.unitWidth + this.gutter) * 1
40167 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40168 y : minY + (this.unitWidth + this.gutter) * 2
40175 if(box[0].size == 'xs' && box[1].size == 'xs'){
40178 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40183 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40188 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40189 y : minY + (this.unitWidth + this.gutter) * 1
40197 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40202 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40203 y : minY + (this.unitWidth + this.gutter) * 2
40207 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40208 y : minY + (this.unitWidth + this.gutter) * 2
40215 getHorizontalFourBoxColPositions : function(maxX, minY, box)
40219 if(box[0].size == 'xs'){
40222 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40227 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40232 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),
40237 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40238 y : minY + (this.unitWidth + this.gutter) * 1
40246 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40251 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40252 y : minY + (this.unitWidth + this.gutter) * 2
40256 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40257 y : minY + (this.unitWidth + this.gutter) * 2
40261 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),
40262 y : minY + (this.unitWidth + this.gutter) * 2
40270 * remove a Masonry Brick
40271 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40273 removeBrick : function(brick_id)
40279 for (var i = 0; i<this.bricks.length; i++) {
40280 if (this.bricks[i].id == brick_id) {
40281 this.bricks.splice(i,1);
40282 this.el.dom.removeChild(Roo.get(brick_id).dom);
40289 * adds a Masonry Brick
40290 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40292 addBrick : function(cfg)
40294 var cn = new Roo.bootstrap.MasonryBrick(cfg);
40295 //this.register(cn);
40296 cn.parentId = this.id;
40297 cn.render(this.el);
40302 * register a Masonry Brick
40303 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40306 register : function(brick)
40308 this.bricks.push(brick);
40309 brick.masonryId = this.id;
40313 * clear all the Masonry Brick
40315 clearAll : function()
40318 //this.getChildContainer().dom.innerHTML = "";
40319 this.el.dom.innerHTML = '';
40322 getSelected : function()
40324 if (!this.selectedBrick) {
40328 return this.selectedBrick;
40332 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40336 * register a Masonry Layout
40337 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40340 register : function(layout)
40342 this.groups[layout.id] = layout;
40345 * fetch a Masonry Layout based on the masonry layout ID
40346 * @param {string} the masonry layout to add
40347 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40350 get: function(layout_id) {
40351 if (typeof(this.groups[layout_id]) == 'undefined') {
40354 return this.groups[layout_id] ;
40366 * http://masonry.desandro.com
40368 * The idea is to render all the bricks based on vertical width...
40370 * The original code extends 'outlayer' - we might need to use that....
40376 * @class Roo.bootstrap.LayoutMasonryAuto
40377 * @extends Roo.bootstrap.Component
40378 * Bootstrap Layout Masonry class
40381 * Create a new Element
40382 * @param {Object} config The config object
40385 Roo.bootstrap.LayoutMasonryAuto = function(config){
40386 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40389 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
40392 * @cfg {Boolean} isFitWidth - resize the width..
40394 isFitWidth : false, // options..
40396 * @cfg {Boolean} isOriginLeft = left align?
40398 isOriginLeft : true,
40400 * @cfg {Boolean} isOriginTop = top align?
40402 isOriginTop : false,
40404 * @cfg {Boolean} isLayoutInstant = no animation?
40406 isLayoutInstant : false, // needed?
40408 * @cfg {Boolean} isResizingContainer = not sure if this is used..
40410 isResizingContainer : true,
40412 * @cfg {Number} columnWidth width of the columns
40418 * @cfg {Number} maxCols maximum number of columns
40423 * @cfg {Number} padHeight padding below box..
40429 * @cfg {Boolean} isAutoInitial defalut true
40432 isAutoInitial : true,
40438 initialColumnWidth : 0,
40439 currentSize : null,
40441 colYs : null, // array.
40448 bricks: null, //CompositeElement
40449 cols : 0, // array?
40450 // element : null, // wrapped now this.el
40451 _isLayoutInited : null,
40454 getAutoCreate : function(){
40458 cls: 'blog-masonary-wrapper ' + this.cls,
40460 cls : 'mas-boxes masonary'
40467 getChildContainer: function( )
40469 if (this.boxesEl) {
40470 return this.boxesEl;
40473 this.boxesEl = this.el.select('.mas-boxes').first();
40475 return this.boxesEl;
40479 initEvents : function()
40483 if(this.isAutoInitial){
40484 Roo.log('hook children rendered');
40485 this.on('childrenrendered', function() {
40486 Roo.log('children rendered');
40493 initial : function()
40495 this.reloadItems();
40497 this.currentSize = this.el.getBox(true);
40499 /// was window resize... - let's see if this works..
40500 Roo.EventManager.onWindowResize(this.resize, this);
40502 if(!this.isAutoInitial){
40507 this.layout.defer(500,this);
40510 reloadItems: function()
40512 this.bricks = this.el.select('.masonry-brick', true);
40514 this.bricks.each(function(b) {
40515 //Roo.log(b.getSize());
40516 if (!b.attr('originalwidth')) {
40517 b.attr('originalwidth', b.getSize().width);
40522 Roo.log(this.bricks.elements.length);
40525 resize : function()
40528 var cs = this.el.getBox(true);
40530 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40531 Roo.log("no change in with or X");
40534 this.currentSize = cs;
40538 layout : function()
40541 this._resetLayout();
40542 //this._manageStamps();
40544 // don't animate first layout
40545 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40546 this.layoutItems( isInstant );
40548 // flag for initalized
40549 this._isLayoutInited = true;
40552 layoutItems : function( isInstant )
40554 //var items = this._getItemsForLayout( this.items );
40555 // original code supports filtering layout items.. we just ignore it..
40557 this._layoutItems( this.bricks , isInstant );
40559 this._postLayout();
40561 _layoutItems : function ( items , isInstant)
40563 //this.fireEvent( 'layout', this, items );
40566 if ( !items || !items.elements.length ) {
40567 // no items, emit event with empty array
40572 items.each(function(item) {
40573 Roo.log("layout item");
40575 // get x/y object from method
40576 var position = this._getItemLayoutPosition( item );
40578 position.item = item;
40579 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40580 queue.push( position );
40583 this._processLayoutQueue( queue );
40585 /** Sets position of item in DOM
40586 * @param {Element} item
40587 * @param {Number} x - horizontal position
40588 * @param {Number} y - vertical position
40589 * @param {Boolean} isInstant - disables transitions
40591 _processLayoutQueue : function( queue )
40593 for ( var i=0, len = queue.length; i < len; i++ ) {
40594 var obj = queue[i];
40595 obj.item.position('absolute');
40596 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40602 * Any logic you want to do after each layout,
40603 * i.e. size the container
40605 _postLayout : function()
40607 this.resizeContainer();
40610 resizeContainer : function()
40612 if ( !this.isResizingContainer ) {
40615 var size = this._getContainerSize();
40617 this.el.setSize(size.width,size.height);
40618 this.boxesEl.setSize(size.width,size.height);
40624 _resetLayout : function()
40626 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40627 this.colWidth = this.el.getWidth();
40628 //this.gutter = this.el.getWidth();
40630 this.measureColumns();
40636 this.colYs.push( 0 );
40642 measureColumns : function()
40644 this.getContainerWidth();
40645 // if columnWidth is 0, default to outerWidth of first item
40646 if ( !this.columnWidth ) {
40647 var firstItem = this.bricks.first();
40648 Roo.log(firstItem);
40649 this.columnWidth = this.containerWidth;
40650 if (firstItem && firstItem.attr('originalwidth') ) {
40651 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40653 // columnWidth fall back to item of first element
40654 Roo.log("set column width?");
40655 this.initialColumnWidth = this.columnWidth ;
40657 // if first elem has no width, default to size of container
40662 if (this.initialColumnWidth) {
40663 this.columnWidth = this.initialColumnWidth;
40668 // column width is fixed at the top - however if container width get's smaller we should
40671 // this bit calcs how man columns..
40673 var columnWidth = this.columnWidth += this.gutter;
40675 // calculate columns
40676 var containerWidth = this.containerWidth + this.gutter;
40678 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40679 // fix rounding errors, typically with gutters
40680 var excess = columnWidth - containerWidth % columnWidth;
40683 // if overshoot is less than a pixel, round up, otherwise floor it
40684 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40685 cols = Math[ mathMethod ]( cols );
40686 this.cols = Math.max( cols, 1 );
40687 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40689 // padding positioning..
40690 var totalColWidth = this.cols * this.columnWidth;
40691 var padavail = this.containerWidth - totalColWidth;
40692 // so for 2 columns - we need 3 'pads'
40694 var padNeeded = (1+this.cols) * this.padWidth;
40696 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40698 this.columnWidth += padExtra
40699 //this.padWidth = Math.floor(padavail / ( this.cols));
40701 // adjust colum width so that padding is fixed??
40703 // we have 3 columns ... total = width * 3
40704 // we have X left over... that should be used by
40706 //if (this.expandC) {
40714 getContainerWidth : function()
40716 /* // container is parent if fit width
40717 var container = this.isFitWidth ? this.element.parentNode : this.element;
40718 // check that this.size and size are there
40719 // IE8 triggers resize on body size change, so they might not be
40721 var size = getSize( container ); //FIXME
40722 this.containerWidth = size && size.innerWidth; //FIXME
40725 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
40729 _getItemLayoutPosition : function( item ) // what is item?
40731 // we resize the item to our columnWidth..
40733 item.setWidth(this.columnWidth);
40734 item.autoBoxAdjust = false;
40736 var sz = item.getSize();
40738 // how many columns does this brick span
40739 var remainder = this.containerWidth % this.columnWidth;
40741 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40742 // round if off by 1 pixel, otherwise use ceil
40743 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
40744 colSpan = Math.min( colSpan, this.cols );
40746 // normally this should be '1' as we dont' currently allow multi width columns..
40748 var colGroup = this._getColGroup( colSpan );
40749 // get the minimum Y value from the columns
40750 var minimumY = Math.min.apply( Math, colGroup );
40751 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40753 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
40755 // position the brick
40757 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40758 y: this.currentSize.y + minimumY + this.padHeight
40762 // apply setHeight to necessary columns
40763 var setHeight = minimumY + sz.height + this.padHeight;
40764 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40766 var setSpan = this.cols + 1 - colGroup.length;
40767 for ( var i = 0; i < setSpan; i++ ) {
40768 this.colYs[ shortColIndex + i ] = setHeight ;
40775 * @param {Number} colSpan - number of columns the element spans
40776 * @returns {Array} colGroup
40778 _getColGroup : function( colSpan )
40780 if ( colSpan < 2 ) {
40781 // if brick spans only one column, use all the column Ys
40786 // how many different places could this brick fit horizontally
40787 var groupCount = this.cols + 1 - colSpan;
40788 // for each group potential horizontal position
40789 for ( var i = 0; i < groupCount; i++ ) {
40790 // make an array of colY values for that one group
40791 var groupColYs = this.colYs.slice( i, i + colSpan );
40792 // and get the max value of the array
40793 colGroup[i] = Math.max.apply( Math, groupColYs );
40798 _manageStamp : function( stamp )
40800 var stampSize = stamp.getSize();
40801 var offset = stamp.getBox();
40802 // get the columns that this stamp affects
40803 var firstX = this.isOriginLeft ? offset.x : offset.right;
40804 var lastX = firstX + stampSize.width;
40805 var firstCol = Math.floor( firstX / this.columnWidth );
40806 firstCol = Math.max( 0, firstCol );
40808 var lastCol = Math.floor( lastX / this.columnWidth );
40809 // lastCol should not go over if multiple of columnWidth #425
40810 lastCol -= lastX % this.columnWidth ? 0 : 1;
40811 lastCol = Math.min( this.cols - 1, lastCol );
40813 // set colYs to bottom of the stamp
40814 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40817 for ( var i = firstCol; i <= lastCol; i++ ) {
40818 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40823 _getContainerSize : function()
40825 this.maxY = Math.max.apply( Math, this.colYs );
40830 if ( this.isFitWidth ) {
40831 size.width = this._getContainerFitWidth();
40837 _getContainerFitWidth : function()
40839 var unusedCols = 0;
40840 // count unused columns
40843 if ( this.colYs[i] !== 0 ) {
40848 // fit container to columns that have been used
40849 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40852 needsResizeLayout : function()
40854 var previousWidth = this.containerWidth;
40855 this.getContainerWidth();
40856 return previousWidth !== this.containerWidth;
40871 * @class Roo.bootstrap.MasonryBrick
40872 * @extends Roo.bootstrap.Component
40873 * Bootstrap MasonryBrick class
40876 * Create a new MasonryBrick
40877 * @param {Object} config The config object
40880 Roo.bootstrap.MasonryBrick = function(config){
40882 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40884 Roo.bootstrap.MasonryBrick.register(this);
40890 * When a MasonryBrick is clcik
40891 * @param {Roo.bootstrap.MasonryBrick} this
40892 * @param {Roo.EventObject} e
40898 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40901 * @cfg {String} title
40905 * @cfg {String} html
40909 * @cfg {String} bgimage
40913 * @cfg {String} videourl
40917 * @cfg {String} cls
40921 * @cfg {String} href
40925 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40930 * @cfg {String} placetitle (center|bottom)
40935 * @cfg {Boolean} isFitContainer defalut true
40937 isFitContainer : true,
40940 * @cfg {Boolean} preventDefault defalut false
40942 preventDefault : false,
40945 * @cfg {Boolean} inverse defalut false
40947 maskInverse : false,
40949 getAutoCreate : function()
40951 if(!this.isFitContainer){
40952 return this.getSplitAutoCreate();
40955 var cls = 'masonry-brick masonry-brick-full';
40957 if(this.href.length){
40958 cls += ' masonry-brick-link';
40961 if(this.bgimage.length){
40962 cls += ' masonry-brick-image';
40965 if(this.maskInverse){
40966 cls += ' mask-inverse';
40969 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40970 cls += ' enable-mask';
40974 cls += ' masonry-' + this.size + '-brick';
40977 if(this.placetitle.length){
40979 switch (this.placetitle) {
40981 cls += ' masonry-center-title';
40984 cls += ' masonry-bottom-title';
40991 if(!this.html.length && !this.bgimage.length){
40992 cls += ' masonry-center-title';
40995 if(!this.html.length && this.bgimage.length){
40996 cls += ' masonry-bottom-title';
41001 cls += ' ' + this.cls;
41005 tag: (this.href.length) ? 'a' : 'div',
41010 cls: 'masonry-brick-mask'
41014 cls: 'masonry-brick-paragraph',
41020 if(this.href.length){
41021 cfg.href = this.href;
41024 var cn = cfg.cn[1].cn;
41026 if(this.title.length){
41029 cls: 'masonry-brick-title',
41034 if(this.html.length){
41037 cls: 'masonry-brick-text',
41042 if (!this.title.length && !this.html.length) {
41043 cfg.cn[1].cls += ' hide';
41046 if(this.bgimage.length){
41049 cls: 'masonry-brick-image-view',
41054 if(this.videourl.length){
41055 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41056 // youtube support only?
41059 cls: 'masonry-brick-image-view',
41062 allowfullscreen : true
41070 getSplitAutoCreate : function()
41072 var cls = 'masonry-brick masonry-brick-split';
41074 if(this.href.length){
41075 cls += ' masonry-brick-link';
41078 if(this.bgimage.length){
41079 cls += ' masonry-brick-image';
41083 cls += ' masonry-' + this.size + '-brick';
41086 switch (this.placetitle) {
41088 cls += ' masonry-center-title';
41091 cls += ' masonry-bottom-title';
41094 if(!this.bgimage.length){
41095 cls += ' masonry-center-title';
41098 if(this.bgimage.length){
41099 cls += ' masonry-bottom-title';
41105 cls += ' ' + this.cls;
41109 tag: (this.href.length) ? 'a' : 'div',
41114 cls: 'masonry-brick-split-head',
41118 cls: 'masonry-brick-paragraph',
41125 cls: 'masonry-brick-split-body',
41131 if(this.href.length){
41132 cfg.href = this.href;
41135 if(this.title.length){
41136 cfg.cn[0].cn[0].cn.push({
41138 cls: 'masonry-brick-title',
41143 if(this.html.length){
41144 cfg.cn[1].cn.push({
41146 cls: 'masonry-brick-text',
41151 if(this.bgimage.length){
41152 cfg.cn[0].cn.push({
41154 cls: 'masonry-brick-image-view',
41159 if(this.videourl.length){
41160 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41161 // youtube support only?
41162 cfg.cn[0].cn.cn.push({
41164 cls: 'masonry-brick-image-view',
41167 allowfullscreen : true
41174 initEvents: function()
41176 switch (this.size) {
41209 this.el.on('touchstart', this.onTouchStart, this);
41210 this.el.on('touchmove', this.onTouchMove, this);
41211 this.el.on('touchend', this.onTouchEnd, this);
41212 this.el.on('contextmenu', this.onContextMenu, this);
41214 this.el.on('mouseenter' ,this.enter, this);
41215 this.el.on('mouseleave', this.leave, this);
41216 this.el.on('click', this.onClick, this);
41219 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41220 this.parent().bricks.push(this);
41225 onClick: function(e, el)
41227 var time = this.endTimer - this.startTimer;
41228 // Roo.log(e.preventDefault());
41231 e.preventDefault();
41236 if(!this.preventDefault){
41240 e.preventDefault();
41242 if (this.activeClass != '') {
41243 this.selectBrick();
41246 this.fireEvent('click', this, e);
41249 enter: function(e, el)
41251 e.preventDefault();
41253 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41257 if(this.bgimage.length && this.html.length){
41258 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41262 leave: function(e, el)
41264 e.preventDefault();
41266 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41270 if(this.bgimage.length && this.html.length){
41271 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41275 onTouchStart: function(e, el)
41277 // e.preventDefault();
41279 this.touchmoved = false;
41281 if(!this.isFitContainer){
41285 if(!this.bgimage.length || !this.html.length){
41289 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41291 this.timer = new Date().getTime();
41295 onTouchMove: function(e, el)
41297 this.touchmoved = true;
41300 onContextMenu : function(e,el)
41302 e.preventDefault();
41303 e.stopPropagation();
41307 onTouchEnd: function(e, el)
41309 // e.preventDefault();
41311 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41318 if(!this.bgimage.length || !this.html.length){
41320 if(this.href.length){
41321 window.location.href = this.href;
41327 if(!this.isFitContainer){
41331 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41333 window.location.href = this.href;
41336 //selection on single brick only
41337 selectBrick : function() {
41339 if (!this.parentId) {
41343 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41344 var index = m.selectedBrick.indexOf(this.id);
41347 m.selectedBrick.splice(index,1);
41348 this.el.removeClass(this.activeClass);
41352 for(var i = 0; i < m.selectedBrick.length; i++) {
41353 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41354 b.el.removeClass(b.activeClass);
41357 m.selectedBrick = [];
41359 m.selectedBrick.push(this.id);
41360 this.el.addClass(this.activeClass);
41364 isSelected : function(){
41365 return this.el.hasClass(this.activeClass);
41370 Roo.apply(Roo.bootstrap.MasonryBrick, {
41373 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41375 * register a Masonry Brick
41376 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41379 register : function(brick)
41381 //this.groups[brick.id] = brick;
41382 this.groups.add(brick.id, brick);
41385 * fetch a masonry brick based on the masonry brick ID
41386 * @param {string} the masonry brick to add
41387 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41390 get: function(brick_id)
41392 // if (typeof(this.groups[brick_id]) == 'undefined') {
41395 // return this.groups[brick_id] ;
41397 if(this.groups.key(brick_id)) {
41398 return this.groups.key(brick_id);
41416 * @class Roo.bootstrap.Brick
41417 * @extends Roo.bootstrap.Component
41418 * Bootstrap Brick class
41421 * Create a new Brick
41422 * @param {Object} config The config object
41425 Roo.bootstrap.Brick = function(config){
41426 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41432 * When a Brick is click
41433 * @param {Roo.bootstrap.Brick} this
41434 * @param {Roo.EventObject} e
41440 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
41443 * @cfg {String} title
41447 * @cfg {String} html
41451 * @cfg {String} bgimage
41455 * @cfg {String} cls
41459 * @cfg {String} href
41463 * @cfg {String} video
41467 * @cfg {Boolean} square
41471 getAutoCreate : function()
41473 var cls = 'roo-brick';
41475 if(this.href.length){
41476 cls += ' roo-brick-link';
41479 if(this.bgimage.length){
41480 cls += ' roo-brick-image';
41483 if(!this.html.length && !this.bgimage.length){
41484 cls += ' roo-brick-center-title';
41487 if(!this.html.length && this.bgimage.length){
41488 cls += ' roo-brick-bottom-title';
41492 cls += ' ' + this.cls;
41496 tag: (this.href.length) ? 'a' : 'div',
41501 cls: 'roo-brick-paragraph',
41507 if(this.href.length){
41508 cfg.href = this.href;
41511 var cn = cfg.cn[0].cn;
41513 if(this.title.length){
41516 cls: 'roo-brick-title',
41521 if(this.html.length){
41524 cls: 'roo-brick-text',
41531 if(this.bgimage.length){
41534 cls: 'roo-brick-image-view',
41542 initEvents: function()
41544 if(this.title.length || this.html.length){
41545 this.el.on('mouseenter' ,this.enter, this);
41546 this.el.on('mouseleave', this.leave, this);
41549 Roo.EventManager.onWindowResize(this.resize, this);
41551 if(this.bgimage.length){
41552 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41553 this.imageEl.on('load', this.onImageLoad, this);
41560 onImageLoad : function()
41565 resize : function()
41567 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41569 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41571 if(this.bgimage.length){
41572 var image = this.el.select('.roo-brick-image-view', true).first();
41574 image.setWidth(paragraph.getWidth());
41577 image.setHeight(paragraph.getWidth());
41580 this.el.setHeight(image.getHeight());
41581 paragraph.setHeight(image.getHeight());
41587 enter: function(e, el)
41589 e.preventDefault();
41591 if(this.bgimage.length){
41592 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41593 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41597 leave: function(e, el)
41599 e.preventDefault();
41601 if(this.bgimage.length){
41602 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41603 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41618 * @class Roo.bootstrap.form.NumberField
41619 * @extends Roo.bootstrap.form.Input
41620 * Bootstrap NumberField class
41626 * Create a new NumberField
41627 * @param {Object} config The config object
41630 Roo.bootstrap.form.NumberField = function(config){
41631 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41634 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41637 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41639 allowDecimals : true,
41641 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41643 decimalSeparator : ".",
41645 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41647 decimalPrecision : 2,
41649 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41651 allowNegative : true,
41654 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41658 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41660 minValue : Number.NEGATIVE_INFINITY,
41662 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41664 maxValue : Number.MAX_VALUE,
41666 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41668 minText : "The minimum value for this field is {0}",
41670 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41672 maxText : "The maximum value for this field is {0}",
41674 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41675 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41677 nanText : "{0} is not a valid number",
41679 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41681 thousandsDelimiter : false,
41683 * @cfg {String} valueAlign alignment of value
41685 valueAlign : "left",
41687 getAutoCreate : function()
41689 var hiddenInput = {
41693 cls: 'hidden-number-input'
41697 hiddenInput.name = this.name;
41702 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41704 this.name = hiddenInput.name;
41706 if(cfg.cn.length > 0) {
41707 cfg.cn.push(hiddenInput);
41714 initEvents : function()
41716 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41718 var allowed = "0123456789";
41720 if(this.allowDecimals){
41721 allowed += this.decimalSeparator;
41724 if(this.allowNegative){
41728 if(this.thousandsDelimiter) {
41732 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41734 var keyPress = function(e){
41736 var k = e.getKey();
41738 var c = e.getCharCode();
41741 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41742 allowed.indexOf(String.fromCharCode(c)) === -1
41748 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41752 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41757 this.el.on("keypress", keyPress, this);
41760 validateValue : function(value)
41763 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41767 var num = this.parseValue(value);
41770 this.markInvalid(String.format(this.nanText, value));
41774 if(num < this.minValue){
41775 this.markInvalid(String.format(this.minText, this.minValue));
41779 if(num > this.maxValue){
41780 this.markInvalid(String.format(this.maxText, this.maxValue));
41787 getValue : function()
41789 var v = this.hiddenEl().getValue();
41791 return this.fixPrecision(this.parseValue(v));
41794 parseValue : function(value)
41796 if(this.thousandsDelimiter) {
41798 r = new RegExp(",", "g");
41799 value = value.replace(r, "");
41802 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41803 return isNaN(value) ? '' : value;
41806 fixPrecision : function(value)
41808 if(this.thousandsDelimiter) {
41810 r = new RegExp(",", "g");
41811 value = value.replace(r, "");
41814 var nan = isNaN(value);
41816 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41817 return nan ? '' : value;
41819 return parseFloat(value).toFixed(this.decimalPrecision);
41822 setValue : function(v)
41824 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41830 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41832 this.inputEl().dom.value = (v == '') ? '' :
41833 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41835 if(!this.allowZero && v === '0') {
41836 this.hiddenEl().dom.value = '';
41837 this.inputEl().dom.value = '';
41844 decimalPrecisionFcn : function(v)
41846 return Math.floor(v);
41849 beforeBlur : function()
41851 var v = this.parseValue(this.getRawValue());
41853 if(v || v === 0 || v === ''){
41858 hiddenEl : function()
41860 return this.el.select('input.hidden-number-input',true).first();
41872 * @class Roo.bootstrap.DocumentSlider
41873 * @extends Roo.bootstrap.Component
41874 * Bootstrap DocumentSlider class
41877 * Create a new DocumentViewer
41878 * @param {Object} config The config object
41881 Roo.bootstrap.DocumentSlider = function(config){
41882 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41889 * Fire after initEvent
41890 * @param {Roo.bootstrap.DocumentSlider} this
41895 * Fire after update
41896 * @param {Roo.bootstrap.DocumentSlider} this
41902 * @param {Roo.bootstrap.DocumentSlider} this
41908 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41914 getAutoCreate : function()
41918 cls : 'roo-document-slider',
41922 cls : 'roo-document-slider-header',
41926 cls : 'roo-document-slider-header-title'
41932 cls : 'roo-document-slider-body',
41936 cls : 'roo-document-slider-prev',
41940 cls : 'fa fa-chevron-left'
41946 cls : 'roo-document-slider-thumb',
41950 cls : 'roo-document-slider-image'
41956 cls : 'roo-document-slider-next',
41960 cls : 'fa fa-chevron-right'
41972 initEvents : function()
41974 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41975 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41977 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41978 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41980 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41981 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41983 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41984 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41986 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41987 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41989 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41990 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41992 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41993 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41995 this.thumbEl.on('click', this.onClick, this);
41997 this.prevIndicator.on('click', this.prev, this);
41999 this.nextIndicator.on('click', this.next, this);
42003 initial : function()
42005 if(this.files.length){
42006 this.indicator = 1;
42010 this.fireEvent('initial', this);
42013 update : function()
42015 this.imageEl.attr('src', this.files[this.indicator - 1]);
42017 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42019 this.prevIndicator.show();
42021 if(this.indicator == 1){
42022 this.prevIndicator.hide();
42025 this.nextIndicator.show();
42027 if(this.indicator == this.files.length){
42028 this.nextIndicator.hide();
42031 this.thumbEl.scrollTo('top');
42033 this.fireEvent('update', this);
42036 onClick : function(e)
42038 e.preventDefault();
42040 this.fireEvent('click', this);
42045 e.preventDefault();
42047 this.indicator = Math.max(1, this.indicator - 1);
42054 e.preventDefault();
42056 this.indicator = Math.min(this.files.length, this.indicator + 1);
42070 * @class Roo.bootstrap.form.RadioSet
42071 * @extends Roo.bootstrap.form.Input
42072 * @children Roo.bootstrap.form.Radio
42073 * Bootstrap RadioSet class
42074 * @cfg {String} indicatorpos (left|right) default left
42075 * @cfg {Boolean} inline (true|false) inline the element (default true)
42076 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42078 * Create a new RadioSet
42079 * @param {Object} config The config object
42082 Roo.bootstrap.form.RadioSet = function(config){
42084 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42088 Roo.bootstrap.form.RadioSet.register(this);
42093 * Fires when the element is checked or unchecked.
42094 * @param {Roo.bootstrap.form.RadioSet} this This radio
42095 * @param {Roo.bootstrap.form.Radio} item The checked item
42100 * Fires when the element is click.
42101 * @param {Roo.bootstrap.form.RadioSet} this This radio set
42102 * @param {Roo.bootstrap.form.Radio} item The checked item
42103 * @param {Roo.EventObject} e The event object
42110 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
42118 indicatorpos : 'left',
42120 getAutoCreate : function()
42124 cls : 'roo-radio-set-label',
42128 html : this.fieldLabel
42132 if (Roo.bootstrap.version == 3) {
42135 if(this.indicatorpos == 'left'){
42138 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42139 tooltip : 'This field is required'
42144 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42145 tooltip : 'This field is required'
42151 cls : 'roo-radio-set-items'
42154 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42156 if (align === 'left' && this.fieldLabel.length) {
42159 cls : "roo-radio-set-right",
42165 if(this.labelWidth > 12){
42166 label.style = "width: " + this.labelWidth + 'px';
42169 if(this.labelWidth < 13 && this.labelmd == 0){
42170 this.labelmd = this.labelWidth;
42173 if(this.labellg > 0){
42174 label.cls += ' col-lg-' + this.labellg;
42175 items.cls += ' col-lg-' + (12 - this.labellg);
42178 if(this.labelmd > 0){
42179 label.cls += ' col-md-' + this.labelmd;
42180 items.cls += ' col-md-' + (12 - this.labelmd);
42183 if(this.labelsm > 0){
42184 label.cls += ' col-sm-' + this.labelsm;
42185 items.cls += ' col-sm-' + (12 - this.labelsm);
42188 if(this.labelxs > 0){
42189 label.cls += ' col-xs-' + this.labelxs;
42190 items.cls += ' col-xs-' + (12 - this.labelxs);
42196 cls : 'roo-radio-set',
42200 cls : 'roo-radio-set-input',
42203 value : this.value ? this.value : ''
42210 if(this.weight.length){
42211 cfg.cls += ' roo-radio-' + this.weight;
42215 cfg.cls += ' roo-radio-set-inline';
42219 ['xs','sm','md','lg'].map(function(size){
42220 if (settings[size]) {
42221 cfg.cls += ' col-' + size + '-' + settings[size];
42229 initEvents : function()
42231 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42232 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42234 if(!this.fieldLabel.length){
42235 this.labelEl.hide();
42238 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42239 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42241 this.indicator = this.indicatorEl();
42243 if(this.indicator){
42244 this.indicator.addClass('invisible');
42247 this.originalValue = this.getValue();
42251 inputEl: function ()
42253 return this.el.select('.roo-radio-set-input', true).first();
42256 getChildContainer : function()
42258 return this.itemsEl;
42261 register : function(item)
42263 this.radioes.push(item);
42267 validate : function()
42269 if(this.getVisibilityEl().hasClass('hidden')){
42275 Roo.each(this.radioes, function(i){
42284 if(this.allowBlank) {
42288 if(this.disabled || valid){
42293 this.markInvalid();
42298 markValid : function()
42300 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42301 this.indicatorEl().removeClass('visible');
42302 this.indicatorEl().addClass('invisible');
42306 if (Roo.bootstrap.version == 3) {
42307 this.el.removeClass([this.invalidClass, this.validClass]);
42308 this.el.addClass(this.validClass);
42310 this.el.removeClass(['is-invalid','is-valid']);
42311 this.el.addClass(['is-valid']);
42313 this.fireEvent('valid', this);
42316 markInvalid : function(msg)
42318 if(this.allowBlank || this.disabled){
42322 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42323 this.indicatorEl().removeClass('invisible');
42324 this.indicatorEl().addClass('visible');
42326 if (Roo.bootstrap.version == 3) {
42327 this.el.removeClass([this.invalidClass, this.validClass]);
42328 this.el.addClass(this.invalidClass);
42330 this.el.removeClass(['is-invalid','is-valid']);
42331 this.el.addClass(['is-invalid']);
42334 this.fireEvent('invalid', this, msg);
42338 setValue : function(v, suppressEvent)
42340 if(this.value === v){
42347 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42350 Roo.each(this.radioes, function(i){
42352 i.el.removeClass('checked');
42355 Roo.each(this.radioes, function(i){
42357 if(i.value === v || i.value.toString() === v.toString()){
42359 i.el.addClass('checked');
42361 if(suppressEvent !== true){
42362 this.fireEvent('check', this, i);
42373 clearInvalid : function(){
42375 if(!this.el || this.preventMark){
42379 this.el.removeClass([this.invalidClass]);
42381 this.fireEvent('valid', this);
42386 Roo.apply(Roo.bootstrap.form.RadioSet, {
42390 register : function(set)
42392 this.groups[set.name] = set;
42395 get: function(name)
42397 if (typeof(this.groups[name]) == 'undefined') {
42401 return this.groups[name] ;
42407 * Ext JS Library 1.1.1
42408 * Copyright(c) 2006-2007, Ext JS, LLC.
42410 * Originally Released Under LGPL - original licence link has changed is not relivant.
42413 * <script type="text/javascript">
42418 * @class Roo.bootstrap.SplitBar
42419 * @extends Roo.util.Observable
42420 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42424 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42425 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42426 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42427 split.minSize = 100;
42428 split.maxSize = 600;
42429 split.animate = true;
42430 split.on('moved', splitterMoved);
42433 * Create a new SplitBar
42434 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
42435 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
42436 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42437 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
42438 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42439 position of the SplitBar).
42441 Roo.bootstrap.SplitBar = function(cfg){
42446 // dragElement : elm
42447 // resizingElement: el,
42449 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42450 // placement : Roo.bootstrap.SplitBar.LEFT ,
42451 // existingProxy ???
42454 this.el = Roo.get(cfg.dragElement, true);
42455 this.el.dom.unselectable = "on";
42457 this.resizingEl = Roo.get(cfg.resizingElement, true);
42461 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42462 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42465 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42468 * The minimum size of the resizing element. (Defaults to 0)
42474 * The maximum size of the resizing element. (Defaults to 2000)
42477 this.maxSize = 2000;
42480 * Whether to animate the transition to the new size
42483 this.animate = false;
42486 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42489 this.useShim = false;
42494 if(!cfg.existingProxy){
42496 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42498 this.proxy = Roo.get(cfg.existingProxy).dom;
42501 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42504 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42507 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42510 this.dragSpecs = {};
42513 * @private The adapter to use to positon and resize elements
42515 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42516 this.adapter.init(this);
42518 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42520 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42521 this.el.addClass("roo-splitbar-h");
42524 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42525 this.el.addClass("roo-splitbar-v");
42531 * Fires when the splitter is moved (alias for {@link #event-moved})
42532 * @param {Roo.bootstrap.SplitBar} this
42533 * @param {Number} newSize the new width or height
42538 * Fires when the splitter is moved
42539 * @param {Roo.bootstrap.SplitBar} this
42540 * @param {Number} newSize the new width or height
42544 * @event beforeresize
42545 * Fires before the splitter is dragged
42546 * @param {Roo.bootstrap.SplitBar} this
42548 "beforeresize" : true,
42550 "beforeapply" : true
42553 Roo.util.Observable.call(this);
42556 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42557 onStartProxyDrag : function(x, y){
42558 this.fireEvent("beforeresize", this);
42560 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
42562 o.enableDisplayMode("block");
42563 // all splitbars share the same overlay
42564 Roo.bootstrap.SplitBar.prototype.overlay = o;
42566 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42567 this.overlay.show();
42568 Roo.get(this.proxy).setDisplayed("block");
42569 var size = this.adapter.getElementSize(this);
42570 this.activeMinSize = this.getMinimumSize();;
42571 this.activeMaxSize = this.getMaximumSize();;
42572 var c1 = size - this.activeMinSize;
42573 var c2 = Math.max(this.activeMaxSize - size, 0);
42574 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42575 this.dd.resetConstraints();
42576 this.dd.setXConstraint(
42577 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
42578 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42580 this.dd.setYConstraint(0, 0);
42582 this.dd.resetConstraints();
42583 this.dd.setXConstraint(0, 0);
42584 this.dd.setYConstraint(
42585 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
42586 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42589 this.dragSpecs.startSize = size;
42590 this.dragSpecs.startPoint = [x, y];
42591 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42595 * @private Called after the drag operation by the DDProxy
42597 onEndProxyDrag : function(e){
42598 Roo.get(this.proxy).setDisplayed(false);
42599 var endPoint = Roo.lib.Event.getXY(e);
42601 this.overlay.hide();
42604 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42605 newSize = this.dragSpecs.startSize +
42606 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42607 endPoint[0] - this.dragSpecs.startPoint[0] :
42608 this.dragSpecs.startPoint[0] - endPoint[0]
42611 newSize = this.dragSpecs.startSize +
42612 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42613 endPoint[1] - this.dragSpecs.startPoint[1] :
42614 this.dragSpecs.startPoint[1] - endPoint[1]
42617 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42618 if(newSize != this.dragSpecs.startSize){
42619 if(this.fireEvent('beforeapply', this, newSize) !== false){
42620 this.adapter.setElementSize(this, newSize);
42621 this.fireEvent("moved", this, newSize);
42622 this.fireEvent("resize", this, newSize);
42628 * Get the adapter this SplitBar uses
42629 * @return The adapter object
42631 getAdapter : function(){
42632 return this.adapter;
42636 * Set the adapter this SplitBar uses
42637 * @param {Object} adapter A SplitBar adapter object
42639 setAdapter : function(adapter){
42640 this.adapter = adapter;
42641 this.adapter.init(this);
42645 * Gets the minimum size for the resizing element
42646 * @return {Number} The minimum size
42648 getMinimumSize : function(){
42649 return this.minSize;
42653 * Sets the minimum size for the resizing element
42654 * @param {Number} minSize The minimum size
42656 setMinimumSize : function(minSize){
42657 this.minSize = minSize;
42661 * Gets the maximum size for the resizing element
42662 * @return {Number} The maximum size
42664 getMaximumSize : function(){
42665 return this.maxSize;
42669 * Sets the maximum size for the resizing element
42670 * @param {Number} maxSize The maximum size
42672 setMaximumSize : function(maxSize){
42673 this.maxSize = maxSize;
42677 * Sets the initialize size for the resizing element
42678 * @param {Number} size The initial size
42680 setCurrentSize : function(size){
42681 var oldAnimate = this.animate;
42682 this.animate = false;
42683 this.adapter.setElementSize(this, size);
42684 this.animate = oldAnimate;
42688 * Destroy this splitbar.
42689 * @param {Boolean} removeEl True to remove the element
42691 destroy : function(removeEl){
42693 this.shim.remove();
42696 this.proxy.parentNode.removeChild(this.proxy);
42704 * @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.
42706 Roo.bootstrap.SplitBar.createProxy = function(dir){
42707 var proxy = new Roo.Element(document.createElement("div"));
42708 proxy.unselectable();
42709 var cls = 'roo-splitbar-proxy';
42710 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42711 document.body.appendChild(proxy.dom);
42716 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42717 * Default Adapter. It assumes the splitter and resizing element are not positioned
42718 * elements and only gets/sets the width of the element. Generally used for table based layouts.
42720 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42723 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42724 // do nothing for now
42725 init : function(s){
42729 * Called before drag operations to get the current size of the resizing element.
42730 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42732 getElementSize : function(s){
42733 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42734 return s.resizingEl.getWidth();
42736 return s.resizingEl.getHeight();
42741 * Called after drag operations to set the size of the resizing element.
42742 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42743 * @param {Number} newSize The new size to set
42744 * @param {Function} onComplete A function to be invoked when resizing is complete
42746 setElementSize : function(s, newSize, onComplete){
42747 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42749 s.resizingEl.setWidth(newSize);
42751 onComplete(s, newSize);
42754 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42759 s.resizingEl.setHeight(newSize);
42761 onComplete(s, newSize);
42764 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42771 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42772 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42773 * Adapter that moves the splitter element to align with the resized sizing element.
42774 * Used with an absolute positioned SplitBar.
42775 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42776 * document.body, make sure you assign an id to the body element.
42778 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42779 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42780 this.container = Roo.get(container);
42783 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42784 init : function(s){
42785 this.basic.init(s);
42788 getElementSize : function(s){
42789 return this.basic.getElementSize(s);
42792 setElementSize : function(s, newSize, onComplete){
42793 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42796 moveSplitter : function(s){
42797 var yes = Roo.bootstrap.SplitBar;
42798 switch(s.placement){
42800 s.el.setX(s.resizingEl.getRight());
42803 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42806 s.el.setY(s.resizingEl.getBottom());
42809 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42816 * Orientation constant - Create a vertical SplitBar
42820 Roo.bootstrap.SplitBar.VERTICAL = 1;
42823 * Orientation constant - Create a horizontal SplitBar
42827 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42830 * Placement constant - The resizing element is to the left of the splitter element
42834 Roo.bootstrap.SplitBar.LEFT = 1;
42837 * Placement constant - The resizing element is to the right of the splitter element
42841 Roo.bootstrap.SplitBar.RIGHT = 2;
42844 * Placement constant - The resizing element is positioned above the splitter element
42848 Roo.bootstrap.SplitBar.TOP = 3;
42851 * Placement constant - The resizing element is positioned under splitter element
42855 Roo.bootstrap.SplitBar.BOTTOM = 4;
42858 * Ext JS Library 1.1.1
42859 * Copyright(c) 2006-2007, Ext JS, LLC.
42861 * Originally Released Under LGPL - original licence link has changed is not relivant.
42864 * <script type="text/javascript">
42868 * @class Roo.bootstrap.layout.Manager
42869 * @extends Roo.bootstrap.Component
42871 * Base class for layout managers.
42873 Roo.bootstrap.layout.Manager = function(config)
42875 this.monitorWindowResize = true; // do this before we apply configuration.
42877 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42883 /** false to disable window resize monitoring @type Boolean */
42889 * Fires when a layout is performed.
42890 * @param {Roo.LayoutManager} this
42894 * @event regionresized
42895 * Fires when the user resizes a region.
42896 * @param {Roo.LayoutRegion} region The resized region
42897 * @param {Number} newSize The new size (width for east/west, height for north/south)
42899 "regionresized" : true,
42901 * @event regioncollapsed
42902 * Fires when a region is collapsed.
42903 * @param {Roo.LayoutRegion} region The collapsed region
42905 "regioncollapsed" : true,
42907 * @event regionexpanded
42908 * Fires when a region is expanded.
42909 * @param {Roo.LayoutRegion} region The expanded region
42911 "regionexpanded" : true
42913 this.updating = false;
42916 this.el = Roo.get(config.el);
42922 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42927 monitorWindowResize : true,
42933 onRender : function(ct, position)
42936 this.el = Roo.get(ct);
42939 //this.fireEvent('render',this);
42943 initEvents: function()
42947 // ie scrollbar fix
42948 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42949 document.body.scroll = "no";
42950 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42951 this.el.position('relative');
42953 this.id = this.el.id;
42954 this.el.addClass("roo-layout-container");
42955 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42956 if(this.el.dom != document.body ) {
42957 this.el.on('resize', this.layout,this);
42958 this.el.on('show', this.layout,this);
42964 * Returns true if this layout is currently being updated
42965 * @return {Boolean}
42967 isUpdating : function(){
42968 return this.updating;
42972 * Suspend the LayoutManager from doing auto-layouts while
42973 * making multiple add or remove calls
42975 beginUpdate : function(){
42976 this.updating = true;
42980 * Restore auto-layouts and optionally disable the manager from performing a layout
42981 * @param {Boolean} noLayout true to disable a layout update
42983 endUpdate : function(noLayout){
42984 this.updating = false;
42990 layout: function(){
42994 onRegionResized : function(region, newSize){
42995 this.fireEvent("regionresized", region, newSize);
42999 onRegionCollapsed : function(region){
43000 this.fireEvent("regioncollapsed", region);
43003 onRegionExpanded : function(region){
43004 this.fireEvent("regionexpanded", region);
43008 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43009 * performs box-model adjustments.
43010 * @return {Object} The size as an object {width: (the width), height: (the height)}
43012 getViewSize : function()
43015 if(this.el.dom != document.body){
43016 size = this.el.getSize();
43018 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43020 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43021 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43026 * Returns the Element this layout is bound to.
43027 * @return {Roo.Element}
43029 getEl : function(){
43034 * Returns the specified region.
43035 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43036 * @return {Roo.LayoutRegion}
43038 getRegion : function(target){
43039 return this.regions[target.toLowerCase()];
43042 onWindowResize : function(){
43043 if(this.monitorWindowResize){
43050 * Ext JS Library 1.1.1
43051 * Copyright(c) 2006-2007, Ext JS, LLC.
43053 * Originally Released Under LGPL - original licence link has changed is not relivant.
43056 * <script type="text/javascript">
43059 * @class Roo.bootstrap.layout.Border
43060 * @extends Roo.bootstrap.layout.Manager
43061 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43062 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43063 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43064 * please see: examples/bootstrap/nested.html<br><br>
43066 <b>The container the layout is rendered into can be either the body element or any other element.
43067 If it is not the body element, the container needs to either be an absolute positioned element,
43068 or you will need to add "position:relative" to the css of the container. You will also need to specify
43069 the container size if it is not the body element.</b>
43072 * Create a new Border
43073 * @param {Object} config Configuration options
43075 Roo.bootstrap.layout.Border = function(config){
43076 config = config || {};
43077 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43081 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43082 if(config[region]){
43083 config[region].region = region;
43084 this.addRegion(config[region]);
43090 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
43092 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43095 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43098 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43101 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43104 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43107 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43113 parent : false, // this might point to a 'nest' or a ???
43116 * Creates and adds a new region if it doesn't already exist.
43117 * @param {String} target The target region key (north, south, east, west or center).
43118 * @param {Object} config The regions config object
43119 * @return {BorderLayoutRegion} The new region
43121 addRegion : function(config)
43123 if(!this.regions[config.region]){
43124 var r = this.factory(config);
43125 this.bindRegion(r);
43127 return this.regions[config.region];
43131 bindRegion : function(r){
43132 this.regions[r.config.region] = r;
43134 r.on("visibilitychange", this.layout, this);
43135 r.on("paneladded", this.layout, this);
43136 r.on("panelremoved", this.layout, this);
43137 r.on("invalidated", this.layout, this);
43138 r.on("resized", this.onRegionResized, this);
43139 r.on("collapsed", this.onRegionCollapsed, this);
43140 r.on("expanded", this.onRegionExpanded, this);
43144 * Performs a layout update.
43146 layout : function()
43148 if(this.updating) {
43152 // render all the rebions if they have not been done alreayd?
43153 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43154 if(this.regions[region] && !this.regions[region].bodyEl){
43155 this.regions[region].onRender(this.el)
43159 var size = this.getViewSize();
43160 var w = size.width;
43161 var h = size.height;
43166 //var x = 0, y = 0;
43168 var rs = this.regions;
43169 var north = rs["north"];
43170 var south = rs["south"];
43171 var west = rs["west"];
43172 var east = rs["east"];
43173 var center = rs["center"];
43174 //if(this.hideOnLayout){ // not supported anymore
43175 //c.el.setStyle("display", "none");
43177 if(north && north.isVisible()){
43178 var b = north.getBox();
43179 var m = north.getMargins();
43180 b.width = w - (m.left+m.right);
43183 centerY = b.height + b.y + m.bottom;
43184 centerH -= centerY;
43185 north.updateBox(this.safeBox(b));
43187 if(south && south.isVisible()){
43188 var b = south.getBox();
43189 var m = south.getMargins();
43190 b.width = w - (m.left+m.right);
43192 var totalHeight = (b.height + m.top + m.bottom);
43193 b.y = h - totalHeight + m.top;
43194 centerH -= totalHeight;
43195 south.updateBox(this.safeBox(b));
43197 if(west && west.isVisible()){
43198 var b = west.getBox();
43199 var m = west.getMargins();
43200 b.height = centerH - (m.top+m.bottom);
43202 b.y = centerY + m.top;
43203 var totalWidth = (b.width + m.left + m.right);
43204 centerX += totalWidth;
43205 centerW -= totalWidth;
43206 west.updateBox(this.safeBox(b));
43208 if(east && east.isVisible()){
43209 var b = east.getBox();
43210 var m = east.getMargins();
43211 b.height = centerH - (m.top+m.bottom);
43212 var totalWidth = (b.width + m.left + m.right);
43213 b.x = w - totalWidth + m.left;
43214 b.y = centerY + m.top;
43215 centerW -= totalWidth;
43216 east.updateBox(this.safeBox(b));
43219 var m = center.getMargins();
43221 x: centerX + m.left,
43222 y: centerY + m.top,
43223 width: centerW - (m.left+m.right),
43224 height: centerH - (m.top+m.bottom)
43226 //if(this.hideOnLayout){
43227 //center.el.setStyle("display", "block");
43229 center.updateBox(this.safeBox(centerBox));
43232 this.fireEvent("layout", this);
43236 safeBox : function(box){
43237 box.width = Math.max(0, box.width);
43238 box.height = Math.max(0, box.height);
43243 * Adds a ContentPanel (or subclass) to this layout.
43244 * @param {String} target The target region key (north, south, east, west or center).
43245 * @param {Roo.ContentPanel} panel The panel to add
43246 * @return {Roo.ContentPanel} The added panel
43248 add : function(target, panel){
43250 target = target.toLowerCase();
43251 return this.regions[target].add(panel);
43255 * Remove a ContentPanel (or subclass) to this layout.
43256 * @param {String} target The target region key (north, south, east, west or center).
43257 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43258 * @return {Roo.ContentPanel} The removed panel
43260 remove : function(target, panel){
43261 target = target.toLowerCase();
43262 return this.regions[target].remove(panel);
43266 * Searches all regions for a panel with the specified id
43267 * @param {String} panelId
43268 * @return {Roo.ContentPanel} The panel or null if it wasn't found
43270 findPanel : function(panelId){
43271 var rs = this.regions;
43272 for(var target in rs){
43273 if(typeof rs[target] != "function"){
43274 var p = rs[target].getPanel(panelId);
43284 * Searches all regions for a panel with the specified id and activates (shows) it.
43285 * @param {String/ContentPanel} panelId The panels id or the panel itself
43286 * @return {Roo.ContentPanel} The shown panel or null
43288 showPanel : function(panelId) {
43289 var rs = this.regions;
43290 for(var target in rs){
43291 var r = rs[target];
43292 if(typeof r != "function"){
43293 if(r.hasPanel(panelId)){
43294 return r.showPanel(panelId);
43302 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43303 * @param {Roo.state.Provider} provider (optional) An alternate state provider
43306 restoreState : function(provider){
43308 provider = Roo.state.Manager;
43310 var sm = new Roo.LayoutStateManager();
43311 sm.init(this, provider);
43317 * Adds a xtype elements to the layout.
43321 xtype : 'ContentPanel',
43328 xtype : 'NestedLayoutPanel',
43334 items : [ ... list of content panels or nested layout panels.. ]
43338 * @param {Object} cfg Xtype definition of item to add.
43340 addxtype : function(cfg)
43342 // basically accepts a pannel...
43343 // can accept a layout region..!?!?
43344 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43347 // theory? children can only be panels??
43349 //if (!cfg.xtype.match(/Panel$/)) {
43354 if (typeof(cfg.region) == 'undefined') {
43355 Roo.log("Failed to add Panel, region was not set");
43359 var region = cfg.region;
43365 xitems = cfg.items;
43370 if ( region == 'center') {
43371 Roo.log("Center: " + cfg.title);
43377 case 'Content': // ContentPanel (el, cfg)
43378 case 'Scroll': // ContentPanel (el, cfg)
43380 cfg.autoCreate = cfg.autoCreate || true;
43381 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43383 // var el = this.el.createChild();
43384 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43387 this.add(region, ret);
43391 case 'TreePanel': // our new panel!
43392 cfg.el = this.el.createChild();
43393 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43394 this.add(region, ret);
43399 // create a new Layout (which is a Border Layout...
43401 var clayout = cfg.layout;
43402 clayout.el = this.el.createChild();
43403 clayout.items = clayout.items || [];
43407 // replace this exitems with the clayout ones..
43408 xitems = clayout.items;
43410 // force background off if it's in center...
43411 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43412 cfg.background = false;
43414 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
43417 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43418 //console.log('adding nested layout panel ' + cfg.toSource());
43419 this.add(region, ret);
43420 nb = {}; /// find first...
43425 // needs grid and region
43427 //var el = this.getRegion(region).el.createChild();
43429 *var el = this.el.createChild();
43430 // create the grid first...
43431 cfg.grid.container = el;
43432 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43435 if (region == 'center' && this.active ) {
43436 cfg.background = false;
43439 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43441 this.add(region, ret);
43443 if (cfg.background) {
43444 // render grid on panel activation (if panel background)
43445 ret.on('activate', function(gp) {
43446 if (!gp.grid.rendered) {
43447 // gp.grid.render(el);
43451 // cfg.grid.render(el);
43457 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43458 // it was the old xcomponent building that caused this before.
43459 // espeically if border is the top element in the tree.
43469 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43471 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43472 this.add(region, ret);
43476 throw "Can not add '" + cfg.xtype + "' to Border";
43482 this.beginUpdate();
43486 Roo.each(xitems, function(i) {
43487 region = nb && i.region ? i.region : false;
43489 var add = ret.addxtype(i);
43492 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43493 if (!i.background) {
43494 abn[region] = nb[region] ;
43501 // make the last non-background panel active..
43502 //if (nb) { Roo.log(abn); }
43505 for(var r in abn) {
43506 region = this.getRegion(r);
43508 // tried using nb[r], but it does not work..
43510 region.showPanel(abn[r]);
43521 factory : function(cfg)
43524 var validRegions = Roo.bootstrap.layout.Border.regions;
43526 var target = cfg.region;
43529 var r = Roo.bootstrap.layout;
43533 return new r.North(cfg);
43535 return new r.South(cfg);
43537 return new r.East(cfg);
43539 return new r.West(cfg);
43541 return new r.Center(cfg);
43543 throw 'Layout region "'+target+'" not supported.';
43550 * Ext JS Library 1.1.1
43551 * Copyright(c) 2006-2007, Ext JS, LLC.
43553 * Originally Released Under LGPL - original licence link has changed is not relivant.
43556 * <script type="text/javascript">
43560 * @class Roo.bootstrap.layout.Basic
43561 * @extends Roo.util.Observable
43562 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43563 * and does not have a titlebar, tabs or any other features. All it does is size and position
43564 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43565 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43566 * @cfg {string} region the region that it inhabits..
43567 * @cfg {bool} skipConfig skip config?
43571 Roo.bootstrap.layout.Basic = function(config){
43573 this.mgr = config.mgr;
43575 this.position = config.region;
43577 var skipConfig = config.skipConfig;
43581 * @scope Roo.BasicLayoutRegion
43585 * @event beforeremove
43586 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43587 * @param {Roo.LayoutRegion} this
43588 * @param {Roo.ContentPanel} panel The panel
43589 * @param {Object} e The cancel event object
43591 "beforeremove" : true,
43593 * @event invalidated
43594 * Fires when the layout for this region is changed.
43595 * @param {Roo.LayoutRegion} this
43597 "invalidated" : true,
43599 * @event visibilitychange
43600 * Fires when this region is shown or hidden
43601 * @param {Roo.LayoutRegion} this
43602 * @param {Boolean} visibility true or false
43604 "visibilitychange" : true,
43606 * @event paneladded
43607 * Fires when a panel is added.
43608 * @param {Roo.LayoutRegion} this
43609 * @param {Roo.ContentPanel} panel The panel
43611 "paneladded" : true,
43613 * @event panelremoved
43614 * Fires when a panel is removed.
43615 * @param {Roo.LayoutRegion} this
43616 * @param {Roo.ContentPanel} panel The panel
43618 "panelremoved" : true,
43620 * @event beforecollapse
43621 * Fires when this region before collapse.
43622 * @param {Roo.LayoutRegion} this
43624 "beforecollapse" : true,
43627 * Fires when this region is collapsed.
43628 * @param {Roo.LayoutRegion} this
43630 "collapsed" : true,
43633 * Fires when this region is expanded.
43634 * @param {Roo.LayoutRegion} this
43639 * Fires when this region is slid into view.
43640 * @param {Roo.LayoutRegion} this
43642 "slideshow" : true,
43645 * Fires when this region slides out of view.
43646 * @param {Roo.LayoutRegion} this
43648 "slidehide" : true,
43650 * @event panelactivated
43651 * Fires when a panel is activated.
43652 * @param {Roo.LayoutRegion} this
43653 * @param {Roo.ContentPanel} panel The activated panel
43655 "panelactivated" : true,
43658 * Fires when the user resizes this region.
43659 * @param {Roo.LayoutRegion} this
43660 * @param {Number} newSize The new size (width for east/west, height for north/south)
43664 /** A collection of panels in this region. @type Roo.util.MixedCollection */
43665 this.panels = new Roo.util.MixedCollection();
43666 this.panels.getKey = this.getPanelId.createDelegate(this);
43668 this.activePanel = null;
43669 // ensure listeners are added...
43671 if (config.listeners || config.events) {
43672 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43673 listeners : config.listeners || {},
43674 events : config.events || {}
43678 if(skipConfig !== true){
43679 this.applyConfig(config);
43683 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43685 getPanelId : function(p){
43689 applyConfig : function(config){
43690 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43691 this.config = config;
43696 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
43697 * the width, for horizontal (north, south) the height.
43698 * @param {Number} newSize The new width or height
43700 resizeTo : function(newSize){
43701 var el = this.el ? this.el :
43702 (this.activePanel ? this.activePanel.getEl() : null);
43704 switch(this.position){
43707 el.setWidth(newSize);
43708 this.fireEvent("resized", this, newSize);
43712 el.setHeight(newSize);
43713 this.fireEvent("resized", this, newSize);
43719 getBox : function(){
43720 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43723 getMargins : function(){
43724 return this.margins;
43727 updateBox : function(box){
43729 var el = this.activePanel.getEl();
43730 el.dom.style.left = box.x + "px";
43731 el.dom.style.top = box.y + "px";
43732 this.activePanel.setSize(box.width, box.height);
43736 * Returns the container element for this region.
43737 * @return {Roo.Element}
43739 getEl : function(){
43740 return this.activePanel;
43744 * Returns true if this region is currently visible.
43745 * @return {Boolean}
43747 isVisible : function(){
43748 return this.activePanel ? true : false;
43751 setActivePanel : function(panel){
43752 panel = this.getPanel(panel);
43753 if(this.activePanel && this.activePanel != panel){
43754 this.activePanel.setActiveState(false);
43755 this.activePanel.getEl().setLeftTop(-10000,-10000);
43757 this.activePanel = panel;
43758 panel.setActiveState(true);
43760 panel.setSize(this.box.width, this.box.height);
43762 this.fireEvent("panelactivated", this, panel);
43763 this.fireEvent("invalidated");
43767 * Show the specified panel.
43768 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43769 * @return {Roo.ContentPanel} The shown panel or null
43771 showPanel : function(panel){
43772 panel = this.getPanel(panel);
43774 this.setActivePanel(panel);
43780 * Get the active panel for this region.
43781 * @return {Roo.ContentPanel} The active panel or null
43783 getActivePanel : function(){
43784 return this.activePanel;
43788 * Add the passed ContentPanel(s)
43789 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43790 * @return {Roo.ContentPanel} The panel added (if only one was added)
43792 add : function(panel){
43793 if(arguments.length > 1){
43794 for(var i = 0, len = arguments.length; i < len; i++) {
43795 this.add(arguments[i]);
43799 if(this.hasPanel(panel)){
43800 this.showPanel(panel);
43803 var el = panel.getEl();
43804 if(el.dom.parentNode != this.mgr.el.dom){
43805 this.mgr.el.dom.appendChild(el.dom);
43807 if(panel.setRegion){
43808 panel.setRegion(this);
43810 this.panels.add(panel);
43811 el.setStyle("position", "absolute");
43812 if(!panel.background){
43813 this.setActivePanel(panel);
43814 if(this.config.initialSize && this.panels.getCount()==1){
43815 this.resizeTo(this.config.initialSize);
43818 this.fireEvent("paneladded", this, panel);
43823 * Returns true if the panel is in this region.
43824 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43825 * @return {Boolean}
43827 hasPanel : function(panel){
43828 if(typeof panel == "object"){ // must be panel obj
43829 panel = panel.getId();
43831 return this.getPanel(panel) ? true : false;
43835 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43836 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43837 * @param {Boolean} preservePanel Overrides the config preservePanel option
43838 * @return {Roo.ContentPanel} The panel that was removed
43840 remove : function(panel, preservePanel){
43841 panel = this.getPanel(panel);
43846 this.fireEvent("beforeremove", this, panel, e);
43847 if(e.cancel === true){
43850 var panelId = panel.getId();
43851 this.panels.removeKey(panelId);
43856 * Returns the panel specified or null if it's not in this region.
43857 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43858 * @return {Roo.ContentPanel}
43860 getPanel : function(id){
43861 if(typeof id == "object"){ // must be panel obj
43864 return this.panels.get(id);
43868 * Returns this regions position (north/south/east/west/center).
43871 getPosition: function(){
43872 return this.position;
43876 * Ext JS Library 1.1.1
43877 * Copyright(c) 2006-2007, Ext JS, LLC.
43879 * Originally Released Under LGPL - original licence link has changed is not relivant.
43882 * <script type="text/javascript">
43886 * @class Roo.bootstrap.layout.Region
43887 * @extends Roo.bootstrap.layout.Basic
43888 * This class represents a region in a layout manager.
43890 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43891 * @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})
43892 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43893 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43894 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43895 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43896 * @cfg {String} title The title for the region (overrides panel titles)
43897 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43898 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43899 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43900 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43901 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43902 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43903 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43904 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43905 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43906 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43908 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43909 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43910 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43911 * @cfg {Number} width For East/West panels
43912 * @cfg {Number} height For North/South panels
43913 * @cfg {Boolean} split To show the splitter
43914 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43916 * @cfg {string} cls Extra CSS classes to add to region
43918 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43919 * @cfg {string} region the region that it inhabits..
43922 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43923 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43925 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43926 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43927 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43929 Roo.bootstrap.layout.Region = function(config)
43931 this.applyConfig(config);
43933 var mgr = config.mgr;
43934 var pos = config.region;
43935 config.skipConfig = true;
43936 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43939 this.onRender(mgr.el);
43942 this.visible = true;
43943 this.collapsed = false;
43944 this.unrendered_panels = [];
43947 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43949 position: '', // set by wrapper (eg. north/south etc..)
43950 unrendered_panels : null, // unrendered panels.
43952 tabPosition : false,
43954 mgr: false, // points to 'Border'
43957 createBody : function(){
43958 /** This region's body element
43959 * @type Roo.Element */
43960 this.bodyEl = this.el.createChild({
43962 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43966 onRender: function(ctr, pos)
43968 var dh = Roo.DomHelper;
43969 /** This region's container element
43970 * @type Roo.Element */
43971 this.el = dh.append(ctr.dom, {
43973 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43975 /** This region's title element
43976 * @type Roo.Element */
43978 this.titleEl = dh.append(this.el.dom, {
43980 unselectable: "on",
43981 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43983 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43984 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43988 this.titleEl.enableDisplayMode();
43989 /** This region's title text element
43990 * @type HTMLElement */
43991 this.titleTextEl = this.titleEl.dom.firstChild;
43992 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43994 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43995 this.closeBtn.enableDisplayMode();
43996 this.closeBtn.on("click", this.closeClicked, this);
43997 this.closeBtn.hide();
43999 this.createBody(this.config);
44000 if(this.config.hideWhenEmpty){
44002 this.on("paneladded", this.validateVisibility, this);
44003 this.on("panelremoved", this.validateVisibility, this);
44005 if(this.autoScroll){
44006 this.bodyEl.setStyle("overflow", "auto");
44008 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44010 //if(c.titlebar !== false){
44011 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44012 this.titleEl.hide();
44014 this.titleEl.show();
44015 if(this.config.title){
44016 this.titleTextEl.innerHTML = this.config.title;
44020 if(this.config.collapsed){
44021 this.collapse(true);
44023 if(this.config.hidden){
44027 if (this.unrendered_panels && this.unrendered_panels.length) {
44028 for (var i =0;i< this.unrendered_panels.length; i++) {
44029 this.add(this.unrendered_panels[i]);
44031 this.unrendered_panels = null;
44037 applyConfig : function(c)
44040 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44041 var dh = Roo.DomHelper;
44042 if(c.titlebar !== false){
44043 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44044 this.collapseBtn.on("click", this.collapse, this);
44045 this.collapseBtn.enableDisplayMode();
44047 if(c.showPin === true || this.showPin){
44048 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44049 this.stickBtn.enableDisplayMode();
44050 this.stickBtn.on("click", this.expand, this);
44051 this.stickBtn.hide();
44056 /** This region's collapsed element
44057 * @type Roo.Element */
44060 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44061 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44064 if(c.floatable !== false){
44065 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44066 this.collapsedEl.on("click", this.collapseClick, this);
44069 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44070 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44071 id: "message", unselectable: "on", style:{"float":"left"}});
44072 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44074 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44075 this.expandBtn.on("click", this.expand, this);
44079 if(this.collapseBtn){
44080 this.collapseBtn.setVisible(c.collapsible == true);
44083 this.cmargins = c.cmargins || this.cmargins ||
44084 (this.position == "west" || this.position == "east" ?
44085 {top: 0, left: 2, right:2, bottom: 0} :
44086 {top: 2, left: 0, right:0, bottom: 2});
44088 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44091 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44093 this.autoScroll = c.autoScroll || false;
44098 this.duration = c.duration || .30;
44099 this.slideDuration = c.slideDuration || .45;
44104 * Returns true if this region is currently visible.
44105 * @return {Boolean}
44107 isVisible : function(){
44108 return this.visible;
44112 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44113 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
44115 //setCollapsedTitle : function(title){
44116 // title = title || " ";
44117 // if(this.collapsedTitleTextEl){
44118 // this.collapsedTitleTextEl.innerHTML = title;
44122 getBox : function(){
44124 // if(!this.collapsed){
44125 b = this.el.getBox(false, true);
44127 // b = this.collapsedEl.getBox(false, true);
44132 getMargins : function(){
44133 return this.margins;
44134 //return this.collapsed ? this.cmargins : this.margins;
44137 highlight : function(){
44138 this.el.addClass("x-layout-panel-dragover");
44141 unhighlight : function(){
44142 this.el.removeClass("x-layout-panel-dragover");
44145 updateBox : function(box)
44147 if (!this.bodyEl) {
44148 return; // not rendered yet..
44152 if(!this.collapsed){
44153 this.el.dom.style.left = box.x + "px";
44154 this.el.dom.style.top = box.y + "px";
44155 this.updateBody(box.width, box.height);
44157 this.collapsedEl.dom.style.left = box.x + "px";
44158 this.collapsedEl.dom.style.top = box.y + "px";
44159 this.collapsedEl.setSize(box.width, box.height);
44162 this.tabs.autoSizeTabs();
44166 updateBody : function(w, h)
44169 this.el.setWidth(w);
44170 w -= this.el.getBorderWidth("rl");
44171 if(this.config.adjustments){
44172 w += this.config.adjustments[0];
44175 if(h !== null && h > 0){
44176 this.el.setHeight(h);
44177 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44178 h -= this.el.getBorderWidth("tb");
44179 if(this.config.adjustments){
44180 h += this.config.adjustments[1];
44182 this.bodyEl.setHeight(h);
44184 h = this.tabs.syncHeight(h);
44187 if(this.panelSize){
44188 w = w !== null ? w : this.panelSize.width;
44189 h = h !== null ? h : this.panelSize.height;
44191 if(this.activePanel){
44192 var el = this.activePanel.getEl();
44193 w = w !== null ? w : el.getWidth();
44194 h = h !== null ? h : el.getHeight();
44195 this.panelSize = {width: w, height: h};
44196 this.activePanel.setSize(w, h);
44198 if(Roo.isIE && this.tabs){
44199 this.tabs.el.repaint();
44204 * Returns the container element for this region.
44205 * @return {Roo.Element}
44207 getEl : function(){
44212 * Hides this region.
44215 //if(!this.collapsed){
44216 this.el.dom.style.left = "-2000px";
44219 // this.collapsedEl.dom.style.left = "-2000px";
44220 // this.collapsedEl.hide();
44222 this.visible = false;
44223 this.fireEvent("visibilitychange", this, false);
44227 * Shows this region if it was previously hidden.
44230 //if(!this.collapsed){
44233 // this.collapsedEl.show();
44235 this.visible = true;
44236 this.fireEvent("visibilitychange", this, true);
44239 closeClicked : function(){
44240 if(this.activePanel){
44241 this.remove(this.activePanel);
44245 collapseClick : function(e){
44247 e.stopPropagation();
44250 e.stopPropagation();
44256 * Collapses this region.
44257 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44260 collapse : function(skipAnim, skipCheck = false){
44261 if(this.collapsed) {
44265 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44267 this.collapsed = true;
44269 this.split.el.hide();
44271 if(this.config.animate && skipAnim !== true){
44272 this.fireEvent("invalidated", this);
44273 this.animateCollapse();
44275 this.el.setLocation(-20000,-20000);
44277 this.collapsedEl.show();
44278 this.fireEvent("collapsed", this);
44279 this.fireEvent("invalidated", this);
44285 animateCollapse : function(){
44290 * Expands this region if it was previously collapsed.
44291 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44292 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44295 expand : function(e, skipAnim){
44297 e.stopPropagation();
44299 if(!this.collapsed || this.el.hasActiveFx()) {
44303 this.afterSlideIn();
44306 this.collapsed = false;
44307 if(this.config.animate && skipAnim !== true){
44308 this.animateExpand();
44312 this.split.el.show();
44314 this.collapsedEl.setLocation(-2000,-2000);
44315 this.collapsedEl.hide();
44316 this.fireEvent("invalidated", this);
44317 this.fireEvent("expanded", this);
44321 animateExpand : function(){
44325 initTabs : function()
44327 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44329 var ts = new Roo.bootstrap.panel.Tabs({
44330 el: this.bodyEl.dom,
44332 tabPosition: this.tabPosition ? this.tabPosition : 'top',
44333 disableTooltips: this.config.disableTabTips,
44334 toolbar : this.config.toolbar
44337 if(this.config.hideTabs){
44338 ts.stripWrap.setDisplayed(false);
44341 ts.resizeTabs = this.config.resizeTabs === true;
44342 ts.minTabWidth = this.config.minTabWidth || 40;
44343 ts.maxTabWidth = this.config.maxTabWidth || 250;
44344 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44345 ts.monitorResize = false;
44346 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44347 ts.bodyEl.addClass('roo-layout-tabs-body');
44348 this.panels.each(this.initPanelAsTab, this);
44351 initPanelAsTab : function(panel){
44352 var ti = this.tabs.addTab(
44356 this.config.closeOnTab && panel.isClosable(),
44359 if(panel.tabTip !== undefined){
44360 ti.setTooltip(panel.tabTip);
44362 ti.on("activate", function(){
44363 this.setActivePanel(panel);
44366 if(this.config.closeOnTab){
44367 ti.on("beforeclose", function(t, e){
44369 this.remove(panel);
44373 panel.tabItem = ti;
44378 updatePanelTitle : function(panel, title)
44380 if(this.activePanel == panel){
44381 this.updateTitle(title);
44384 var ti = this.tabs.getTab(panel.getEl().id);
44386 if(panel.tabTip !== undefined){
44387 ti.setTooltip(panel.tabTip);
44392 updateTitle : function(title){
44393 if(this.titleTextEl && !this.config.title){
44394 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
44398 setActivePanel : function(panel)
44400 panel = this.getPanel(panel);
44401 if(this.activePanel && this.activePanel != panel){
44402 if(this.activePanel.setActiveState(false) === false){
44406 this.activePanel = panel;
44407 panel.setActiveState(true);
44408 if(this.panelSize){
44409 panel.setSize(this.panelSize.width, this.panelSize.height);
44412 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44414 this.updateTitle(panel.getTitle());
44416 this.fireEvent("invalidated", this);
44418 this.fireEvent("panelactivated", this, panel);
44422 * Shows the specified panel.
44423 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44424 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44426 showPanel : function(panel)
44428 panel = this.getPanel(panel);
44431 var tab = this.tabs.getTab(panel.getEl().id);
44432 if(tab.isHidden()){
44433 this.tabs.unhideTab(tab.id);
44437 this.setActivePanel(panel);
44444 * Get the active panel for this region.
44445 * @return {Roo.ContentPanel} The active panel or null
44447 getActivePanel : function(){
44448 return this.activePanel;
44451 validateVisibility : function(){
44452 if(this.panels.getCount() < 1){
44453 this.updateTitle(" ");
44454 this.closeBtn.hide();
44457 if(!this.isVisible()){
44464 * Adds the passed ContentPanel(s) to this region.
44465 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44466 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44468 add : function(panel)
44470 if(arguments.length > 1){
44471 for(var i = 0, len = arguments.length; i < len; i++) {
44472 this.add(arguments[i]);
44477 // if we have not been rendered yet, then we can not really do much of this..
44478 if (!this.bodyEl) {
44479 this.unrendered_panels.push(panel);
44486 if(this.hasPanel(panel)){
44487 this.showPanel(panel);
44490 panel.setRegion(this);
44491 this.panels.add(panel);
44492 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44493 // sinle panel - no tab...?? would it not be better to render it with the tabs,
44494 // and hide them... ???
44495 this.bodyEl.dom.appendChild(panel.getEl().dom);
44496 if(panel.background !== true){
44497 this.setActivePanel(panel);
44499 this.fireEvent("paneladded", this, panel);
44506 this.initPanelAsTab(panel);
44510 if(panel.background !== true){
44511 this.tabs.activate(panel.getEl().id);
44513 this.fireEvent("paneladded", this, panel);
44518 * Hides the tab for the specified panel.
44519 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44521 hidePanel : function(panel){
44522 if(this.tabs && (panel = this.getPanel(panel))){
44523 this.tabs.hideTab(panel.getEl().id);
44528 * Unhides the tab for a previously hidden panel.
44529 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44531 unhidePanel : function(panel){
44532 if(this.tabs && (panel = this.getPanel(panel))){
44533 this.tabs.unhideTab(panel.getEl().id);
44537 clearPanels : function(){
44538 while(this.panels.getCount() > 0){
44539 this.remove(this.panels.first());
44544 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44545 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44546 * @param {Boolean} preservePanel Overrides the config preservePanel option
44547 * @return {Roo.ContentPanel} The panel that was removed
44549 remove : function(panel, preservePanel)
44551 panel = this.getPanel(panel);
44556 this.fireEvent("beforeremove", this, panel, e);
44557 if(e.cancel === true){
44560 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44561 var panelId = panel.getId();
44562 this.panels.removeKey(panelId);
44564 document.body.appendChild(panel.getEl().dom);
44567 this.tabs.removeTab(panel.getEl().id);
44568 }else if (!preservePanel){
44569 this.bodyEl.dom.removeChild(panel.getEl().dom);
44571 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44572 var p = this.panels.first();
44573 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44574 tempEl.appendChild(p.getEl().dom);
44575 this.bodyEl.update("");
44576 this.bodyEl.dom.appendChild(p.getEl().dom);
44578 this.updateTitle(p.getTitle());
44580 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44581 this.setActivePanel(p);
44583 panel.setRegion(null);
44584 if(this.activePanel == panel){
44585 this.activePanel = null;
44587 if(this.config.autoDestroy !== false && preservePanel !== true){
44588 try{panel.destroy();}catch(e){}
44590 this.fireEvent("panelremoved", this, panel);
44595 * Returns the TabPanel component used by this region
44596 * @return {Roo.TabPanel}
44598 getTabs : function(){
44602 createTool : function(parentEl, className){
44603 var btn = Roo.DomHelper.append(parentEl, {
44605 cls: "x-layout-tools-button",
44608 cls: "roo-layout-tools-button-inner " + className,
44612 btn.addClassOnOver("roo-layout-tools-button-over");
44617 * Ext JS Library 1.1.1
44618 * Copyright(c) 2006-2007, Ext JS, LLC.
44620 * Originally Released Under LGPL - original licence link has changed is not relivant.
44623 * <script type="text/javascript">
44629 * @class Roo.SplitLayoutRegion
44630 * @extends Roo.LayoutRegion
44631 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44633 Roo.bootstrap.layout.Split = function(config){
44634 this.cursor = config.cursor;
44635 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44638 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44640 splitTip : "Drag to resize.",
44641 collapsibleSplitTip : "Drag to resize. Double click to hide.",
44642 useSplitTips : false,
44644 applyConfig : function(config){
44645 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44648 onRender : function(ctr,pos) {
44650 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44651 if(!this.config.split){
44656 var splitEl = Roo.DomHelper.append(ctr.dom, {
44658 id: this.el.id + "-split",
44659 cls: "roo-layout-split roo-layout-split-"+this.position,
44662 /** The SplitBar for this region
44663 * @type Roo.SplitBar */
44664 // does not exist yet...
44665 Roo.log([this.position, this.orientation]);
44667 this.split = new Roo.bootstrap.SplitBar({
44668 dragElement : splitEl,
44669 resizingElement: this.el,
44670 orientation : this.orientation
44673 this.split.on("moved", this.onSplitMove, this);
44674 this.split.useShim = this.config.useShim === true;
44675 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44676 if(this.useSplitTips){
44677 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44679 //if(config.collapsible){
44680 // this.split.el.on("dblclick", this.collapse, this);
44683 if(typeof this.config.minSize != "undefined"){
44684 this.split.minSize = this.config.minSize;
44686 if(typeof this.config.maxSize != "undefined"){
44687 this.split.maxSize = this.config.maxSize;
44689 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44690 this.hideSplitter();
44695 getHMaxSize : function(){
44696 var cmax = this.config.maxSize || 10000;
44697 var center = this.mgr.getRegion("center");
44698 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44701 getVMaxSize : function(){
44702 var cmax = this.config.maxSize || 10000;
44703 var center = this.mgr.getRegion("center");
44704 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44707 onSplitMove : function(split, newSize){
44708 this.fireEvent("resized", this, newSize);
44712 * Returns the {@link Roo.SplitBar} for this region.
44713 * @return {Roo.SplitBar}
44715 getSplitBar : function(){
44720 this.hideSplitter();
44721 Roo.bootstrap.layout.Split.superclass.hide.call(this);
44724 hideSplitter : function(){
44726 this.split.el.setLocation(-2000,-2000);
44727 this.split.el.hide();
44733 this.split.el.show();
44735 Roo.bootstrap.layout.Split.superclass.show.call(this);
44738 beforeSlide: function(){
44739 if(Roo.isGecko){// firefox overflow auto bug workaround
44740 this.bodyEl.clip();
44742 this.tabs.bodyEl.clip();
44744 if(this.activePanel){
44745 this.activePanel.getEl().clip();
44747 if(this.activePanel.beforeSlide){
44748 this.activePanel.beforeSlide();
44754 afterSlide : function(){
44755 if(Roo.isGecko){// firefox overflow auto bug workaround
44756 this.bodyEl.unclip();
44758 this.tabs.bodyEl.unclip();
44760 if(this.activePanel){
44761 this.activePanel.getEl().unclip();
44762 if(this.activePanel.afterSlide){
44763 this.activePanel.afterSlide();
44769 initAutoHide : function(){
44770 if(this.autoHide !== false){
44771 if(!this.autoHideHd){
44772 var st = new Roo.util.DelayedTask(this.slideIn, this);
44773 this.autoHideHd = {
44774 "mouseout": function(e){
44775 if(!e.within(this.el, true)){
44779 "mouseover" : function(e){
44785 this.el.on(this.autoHideHd);
44789 clearAutoHide : function(){
44790 if(this.autoHide !== false){
44791 this.el.un("mouseout", this.autoHideHd.mouseout);
44792 this.el.un("mouseover", this.autoHideHd.mouseover);
44796 clearMonitor : function(){
44797 Roo.get(document).un("click", this.slideInIf, this);
44800 // these names are backwards but not changed for compat
44801 slideOut : function(){
44802 if(this.isSlid || this.el.hasActiveFx()){
44805 this.isSlid = true;
44806 if(this.collapseBtn){
44807 this.collapseBtn.hide();
44809 this.closeBtnState = this.closeBtn.getStyle('display');
44810 this.closeBtn.hide();
44812 this.stickBtn.show();
44815 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44816 this.beforeSlide();
44817 this.el.setStyle("z-index", 10001);
44818 this.el.slideIn(this.getSlideAnchor(), {
44819 callback: function(){
44821 this.initAutoHide();
44822 Roo.get(document).on("click", this.slideInIf, this);
44823 this.fireEvent("slideshow", this);
44830 afterSlideIn : function(){
44831 this.clearAutoHide();
44832 this.isSlid = false;
44833 this.clearMonitor();
44834 this.el.setStyle("z-index", "");
44835 if(this.collapseBtn){
44836 this.collapseBtn.show();
44838 this.closeBtn.setStyle('display', this.closeBtnState);
44840 this.stickBtn.hide();
44842 this.fireEvent("slidehide", this);
44845 slideIn : function(cb){
44846 if(!this.isSlid || this.el.hasActiveFx()){
44850 this.isSlid = false;
44851 this.beforeSlide();
44852 this.el.slideOut(this.getSlideAnchor(), {
44853 callback: function(){
44854 this.el.setLeftTop(-10000, -10000);
44856 this.afterSlideIn();
44864 slideInIf : function(e){
44865 if(!e.within(this.el)){
44870 animateCollapse : function(){
44871 this.beforeSlide();
44872 this.el.setStyle("z-index", 20000);
44873 var anchor = this.getSlideAnchor();
44874 this.el.slideOut(anchor, {
44875 callback : function(){
44876 this.el.setStyle("z-index", "");
44877 this.collapsedEl.slideIn(anchor, {duration:.3});
44879 this.el.setLocation(-10000,-10000);
44881 this.fireEvent("collapsed", this);
44888 animateExpand : function(){
44889 this.beforeSlide();
44890 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44891 this.el.setStyle("z-index", 20000);
44892 this.collapsedEl.hide({
44895 this.el.slideIn(this.getSlideAnchor(), {
44896 callback : function(){
44897 this.el.setStyle("z-index", "");
44900 this.split.el.show();
44902 this.fireEvent("invalidated", this);
44903 this.fireEvent("expanded", this);
44931 getAnchor : function(){
44932 return this.anchors[this.position];
44935 getCollapseAnchor : function(){
44936 return this.canchors[this.position];
44939 getSlideAnchor : function(){
44940 return this.sanchors[this.position];
44943 getAlignAdj : function(){
44944 var cm = this.cmargins;
44945 switch(this.position){
44961 getExpandAdj : function(){
44962 var c = this.collapsedEl, cm = this.cmargins;
44963 switch(this.position){
44965 return [-(cm.right+c.getWidth()+cm.left), 0];
44968 return [cm.right+c.getWidth()+cm.left, 0];
44971 return [0, -(cm.top+cm.bottom+c.getHeight())];
44974 return [0, cm.top+cm.bottom+c.getHeight()];
44980 * Ext JS Library 1.1.1
44981 * Copyright(c) 2006-2007, Ext JS, LLC.
44983 * Originally Released Under LGPL - original licence link has changed is not relivant.
44986 * <script type="text/javascript">
44989 * These classes are private internal classes
44991 Roo.bootstrap.layout.Center = function(config){
44992 config.region = "center";
44993 Roo.bootstrap.layout.Region.call(this, config);
44994 this.visible = true;
44995 this.minWidth = config.minWidth || 20;
44996 this.minHeight = config.minHeight || 20;
44999 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
45001 // center panel can't be hidden
45005 // center panel can't be hidden
45008 getMinWidth: function(){
45009 return this.minWidth;
45012 getMinHeight: function(){
45013 return this.minHeight;
45027 Roo.bootstrap.layout.North = function(config)
45029 config.region = 'north';
45030 config.cursor = 'n-resize';
45032 Roo.bootstrap.layout.Split.call(this, config);
45036 this.split.placement = Roo.bootstrap.SplitBar.TOP;
45037 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45038 this.split.el.addClass("roo-layout-split-v");
45040 //var size = config.initialSize || config.height;
45041 //if(this.el && typeof size != "undefined"){
45042 // this.el.setHeight(size);
45045 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45047 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45050 onRender : function(ctr, pos)
45052 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45053 var size = this.config.initialSize || this.config.height;
45054 if(this.el && typeof size != "undefined"){
45055 this.el.setHeight(size);
45060 getBox : function(){
45061 if(this.collapsed){
45062 return this.collapsedEl.getBox();
45064 var box = this.el.getBox();
45066 box.height += this.split.el.getHeight();
45071 updateBox : function(box){
45072 if(this.split && !this.collapsed){
45073 box.height -= this.split.el.getHeight();
45074 this.split.el.setLeft(box.x);
45075 this.split.el.setTop(box.y+box.height);
45076 this.split.el.setWidth(box.width);
45078 if(this.collapsed){
45079 this.updateBody(box.width, null);
45081 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45089 Roo.bootstrap.layout.South = function(config){
45090 config.region = 'south';
45091 config.cursor = 's-resize';
45092 Roo.bootstrap.layout.Split.call(this, config);
45094 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45095 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45096 this.split.el.addClass("roo-layout-split-v");
45101 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45102 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45104 onRender : function(ctr, pos)
45106 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45107 var size = this.config.initialSize || this.config.height;
45108 if(this.el && typeof size != "undefined"){
45109 this.el.setHeight(size);
45114 getBox : function(){
45115 if(this.collapsed){
45116 return this.collapsedEl.getBox();
45118 var box = this.el.getBox();
45120 var sh = this.split.el.getHeight();
45127 updateBox : function(box){
45128 if(this.split && !this.collapsed){
45129 var sh = this.split.el.getHeight();
45132 this.split.el.setLeft(box.x);
45133 this.split.el.setTop(box.y-sh);
45134 this.split.el.setWidth(box.width);
45136 if(this.collapsed){
45137 this.updateBody(box.width, null);
45139 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45143 Roo.bootstrap.layout.East = function(config){
45144 config.region = "east";
45145 config.cursor = "e-resize";
45146 Roo.bootstrap.layout.Split.call(this, config);
45148 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45149 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45150 this.split.el.addClass("roo-layout-split-h");
45154 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45155 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45157 onRender : function(ctr, pos)
45159 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45160 var size = this.config.initialSize || this.config.width;
45161 if(this.el && typeof size != "undefined"){
45162 this.el.setWidth(size);
45167 getBox : function(){
45168 if(this.collapsed){
45169 return this.collapsedEl.getBox();
45171 var box = this.el.getBox();
45173 var sw = this.split.el.getWidth();
45180 updateBox : function(box){
45181 if(this.split && !this.collapsed){
45182 var sw = this.split.el.getWidth();
45184 this.split.el.setLeft(box.x);
45185 this.split.el.setTop(box.y);
45186 this.split.el.setHeight(box.height);
45189 if(this.collapsed){
45190 this.updateBody(null, box.height);
45192 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45196 Roo.bootstrap.layout.West = function(config){
45197 config.region = "west";
45198 config.cursor = "w-resize";
45200 Roo.bootstrap.layout.Split.call(this, config);
45202 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45203 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45204 this.split.el.addClass("roo-layout-split-h");
45208 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45209 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45211 onRender: function(ctr, pos)
45213 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45214 var size = this.config.initialSize || this.config.width;
45215 if(typeof size != "undefined"){
45216 this.el.setWidth(size);
45220 getBox : function(){
45221 if(this.collapsed){
45222 return this.collapsedEl.getBox();
45224 var box = this.el.getBox();
45225 if (box.width == 0) {
45226 box.width = this.config.width; // kludge?
45229 box.width += this.split.el.getWidth();
45234 updateBox : function(box){
45235 if(this.split && !this.collapsed){
45236 var sw = this.split.el.getWidth();
45238 this.split.el.setLeft(box.x+box.width);
45239 this.split.el.setTop(box.y);
45240 this.split.el.setHeight(box.height);
45242 if(this.collapsed){
45243 this.updateBody(null, box.height);
45245 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45249 * Ext JS Library 1.1.1
45250 * Copyright(c) 2006-2007, Ext JS, LLC.
45252 * Originally Released Under LGPL - original licence link has changed is not relivant.
45255 * <script type="text/javascript">
45258 * @class Roo.bootstrap.paenl.Content
45259 * @extends Roo.util.Observable
45260 * @children Roo.bootstrap.Component
45261 * @parent builder Roo.bootstrap.layout.Border
45262 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45263 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
45264 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
45265 * @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
45266 * @cfg {Boolean} closable True if the panel can be closed/removed
45267 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45268 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45269 * @cfg {Toolbar} toolbar A toolbar for this panel
45270 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45271 * @cfg {String} title The title for this panel
45272 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45273 * @cfg {String} url Calls {@link #setUrl} with this value
45274 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45275 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45276 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45277 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
45278 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
45279 * @cfg {Boolean} badges render the badges
45280 * @cfg {String} cls extra classes to use
45281 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45284 * Create a new ContentPanel.
45285 * @param {String/Object} config A string to set only the title or a config object
45288 Roo.bootstrap.panel.Content = function( config){
45290 this.tpl = config.tpl || false;
45292 var el = config.el;
45293 var content = config.content;
45295 if(config.autoCreate){ // xtype is available if this is called from factory
45298 this.el = Roo.get(el);
45299 if(!this.el && config && config.autoCreate){
45300 if(typeof config.autoCreate == "object"){
45301 if(!config.autoCreate.id){
45302 config.autoCreate.id = config.id||el;
45304 this.el = Roo.DomHelper.append(document.body,
45305 config.autoCreate, true);
45309 cls: (config.cls || '') +
45310 (config.background ? ' bg-' + config.background : '') +
45311 " roo-layout-inactive-content",
45314 if (config.iframe) {
45318 style : 'border: 0px',
45319 src : 'about:blank'
45325 elcfg.html = config.html;
45329 this.el = Roo.DomHelper.append(document.body, elcfg , true);
45330 if (config.iframe) {
45331 this.iframeEl = this.el.select('iframe',true).first();
45336 this.closable = false;
45337 this.loaded = false;
45338 this.active = false;
45341 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45343 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45345 this.wrapEl = this.el; //this.el.wrap();
45347 if (config.toolbar.items) {
45348 ti = config.toolbar.items ;
45349 delete config.toolbar.items ;
45353 this.toolbar.render(this.wrapEl, 'before');
45354 for(var i =0;i < ti.length;i++) {
45355 // Roo.log(['add child', items[i]]);
45356 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45358 this.toolbar.items = nitems;
45359 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45360 delete config.toolbar;
45364 // xtype created footer. - not sure if will work as we normally have to render first..
45365 if (this.footer && !this.footer.el && this.footer.xtype) {
45366 if (!this.wrapEl) {
45367 this.wrapEl = this.el.wrap();
45370 this.footer.container = this.wrapEl.createChild();
45372 this.footer = Roo.factory(this.footer, Roo);
45377 if(typeof config == "string"){
45378 this.title = config;
45380 Roo.apply(this, config);
45384 this.resizeEl = Roo.get(this.resizeEl, true);
45386 this.resizeEl = this.el;
45388 // handle view.xtype
45396 * Fires when this panel is activated.
45397 * @param {Roo.ContentPanel} this
45401 * @event deactivate
45402 * Fires when this panel is activated.
45403 * @param {Roo.ContentPanel} this
45405 "deactivate" : true,
45409 * Fires when this panel is resized if fitToFrame is true.
45410 * @param {Roo.ContentPanel} this
45411 * @param {Number} width The width after any component adjustments
45412 * @param {Number} height The height after any component adjustments
45418 * Fires when this tab is created
45419 * @param {Roo.ContentPanel} this
45425 * Fires when this content is scrolled
45426 * @param {Roo.ContentPanel} this
45427 * @param {Event} scrollEvent
45438 if(this.autoScroll && !this.iframe){
45439 this.resizeEl.setStyle("overflow", "auto");
45440 this.resizeEl.on('scroll', this.onScroll, this);
45442 // fix randome scrolling
45443 //this.el.on('scroll', function() {
45444 // Roo.log('fix random scolling');
45445 // this.scrollTo('top',0);
45448 content = content || this.content;
45450 this.setContent(content);
45452 if(config && config.url){
45453 this.setUrl(this.url, this.params, this.loadOnce);
45458 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45460 if (this.view && typeof(this.view.xtype) != 'undefined') {
45461 this.view.el = this.el.appendChild(document.createElement("div"));
45462 this.view = Roo.factory(this.view);
45463 this.view.render && this.view.render(false, '');
45467 this.fireEvent('render', this);
45470 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45480 /* Resize Element - use this to work out scroll etc. */
45483 setRegion : function(region){
45484 this.region = region;
45485 this.setActiveClass(region && !this.background);
45489 setActiveClass: function(state)
45492 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45493 this.el.setStyle('position','relative');
45495 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45496 this.el.setStyle('position', 'absolute');
45501 * Returns the toolbar for this Panel if one was configured.
45502 * @return {Roo.Toolbar}
45504 getToolbar : function(){
45505 return this.toolbar;
45508 setActiveState : function(active)
45510 this.active = active;
45511 this.setActiveClass(active);
45513 if(this.fireEvent("deactivate", this) === false){
45518 this.fireEvent("activate", this);
45522 * Updates this panel's element (not for iframe)
45523 * @param {String} content The new content
45524 * @param {Boolean} loadScripts (optional) true to look for and process scripts
45526 setContent : function(content, loadScripts){
45531 this.el.update(content, loadScripts);
45534 ignoreResize : function(w, h)
45536 //return false; // always resize?
45537 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45540 this.lastSize = {width: w, height: h};
45545 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45546 * @return {Roo.UpdateManager} The UpdateManager
45548 getUpdateManager : function(){
45552 return this.el.getUpdateManager();
45555 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45556 * Does not work with IFRAME contents
45557 * @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:
45560 url: "your-url.php",
45561 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45562 callback: yourFunction,
45563 scope: yourObject, //(optional scope)
45566 text: "Loading...",
45572 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45573 * 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.
45574 * @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}
45575 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45576 * @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.
45577 * @return {Roo.ContentPanel} this
45585 var um = this.el.getUpdateManager();
45586 um.update.apply(um, arguments);
45592 * 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.
45593 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45594 * @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)
45595 * @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)
45596 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45598 setUrl : function(url, params, loadOnce){
45600 this.iframeEl.dom.src = url;
45604 if(this.refreshDelegate){
45605 this.removeListener("activate", this.refreshDelegate);
45607 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45608 this.on("activate", this.refreshDelegate);
45609 return this.el.getUpdateManager();
45612 _handleRefresh : function(url, params, loadOnce){
45613 if(!loadOnce || !this.loaded){
45614 var updater = this.el.getUpdateManager();
45615 updater.update(url, params, this._setLoaded.createDelegate(this));
45619 _setLoaded : function(){
45620 this.loaded = true;
45624 * Returns this panel's id
45627 getId : function(){
45632 * Returns this panel's element - used by regiosn to add.
45633 * @return {Roo.Element}
45635 getEl : function(){
45636 return this.wrapEl || this.el;
45641 adjustForComponents : function(width, height)
45643 //Roo.log('adjustForComponents ');
45644 if(this.resizeEl != this.el){
45645 width -= this.el.getFrameWidth('lr');
45646 height -= this.el.getFrameWidth('tb');
45649 var te = this.toolbar.getEl();
45650 te.setWidth(width);
45651 height -= te.getHeight();
45654 var te = this.footer.getEl();
45655 te.setWidth(width);
45656 height -= te.getHeight();
45660 if(this.adjustments){
45661 width += this.adjustments[0];
45662 height += this.adjustments[1];
45664 return {"width": width, "height": height};
45667 setSize : function(width, height){
45668 if(this.fitToFrame && !this.ignoreResize(width, height)){
45669 if(this.fitContainer && this.resizeEl != this.el){
45670 this.el.setSize(width, height);
45672 var size = this.adjustForComponents(width, height);
45674 this.iframeEl.setSize(width,height);
45677 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45678 this.fireEvent('resize', this, size.width, size.height);
45685 * Returns this panel's title
45688 getTitle : function(){
45690 if (typeof(this.title) != 'object') {
45695 for (var k in this.title) {
45696 if (!this.title.hasOwnProperty(k)) {
45700 if (k.indexOf('-') >= 0) {
45701 var s = k.split('-');
45702 for (var i = 0; i<s.length; i++) {
45703 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45706 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45713 * Set this panel's title
45714 * @param {String} title
45716 setTitle : function(title){
45717 this.title = title;
45719 this.region.updatePanelTitle(this, title);
45724 * Returns true is this panel was configured to be closable
45725 * @return {Boolean}
45727 isClosable : function(){
45728 return this.closable;
45731 beforeSlide : function(){
45733 this.resizeEl.clip();
45736 afterSlide : function(){
45738 this.resizeEl.unclip();
45742 * Force a content refresh from the URL specified in the {@link #setUrl} method.
45743 * Will fail silently if the {@link #setUrl} method has not been called.
45744 * This does not activate the panel, just updates its content.
45746 refresh : function(){
45747 if(this.refreshDelegate){
45748 this.loaded = false;
45749 this.refreshDelegate();
45754 * Destroys this panel
45756 destroy : function(){
45757 this.el.removeAllListeners();
45758 var tempEl = document.createElement("span");
45759 tempEl.appendChild(this.el.dom);
45760 tempEl.innerHTML = "";
45766 * form - if the content panel contains a form - this is a reference to it.
45767 * @type {Roo.form.Form}
45771 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45772 * This contains a reference to it.
45778 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45788 * @param {Object} cfg Xtype definition of item to add.
45792 getChildContainer: function () {
45793 return this.getEl();
45797 onScroll : function(e)
45799 this.fireEvent('scroll', this, e);
45804 var ret = new Roo.factory(cfg);
45809 if (cfg.xtype.match(/^Form$/)) {
45812 //if (this.footer) {
45813 // el = this.footer.container.insertSibling(false, 'before');
45815 el = this.el.createChild();
45818 this.form = new Roo.form.Form(cfg);
45821 if ( this.form.allItems.length) {
45822 this.form.render(el.dom);
45826 // should only have one of theses..
45827 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45828 // views.. should not be just added - used named prop 'view''
45830 cfg.el = this.el.appendChild(document.createElement("div"));
45833 var ret = new Roo.factory(cfg);
45835 ret.render && ret.render(false, ''); // render blank..
45845 * @class Roo.bootstrap.panel.Grid
45846 * @extends Roo.bootstrap.panel.Content
45848 * Create a new GridPanel.
45849 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45850 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45851 * @param {Object} config A the config object
45857 Roo.bootstrap.panel.Grid = function(config)
45861 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45862 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45864 config.el = this.wrapper;
45865 //this.el = this.wrapper;
45867 if (config.container) {
45868 // ctor'ed from a Border/panel.grid
45871 this.wrapper.setStyle("overflow", "hidden");
45872 this.wrapper.addClass('roo-grid-container');
45877 if(config.toolbar){
45878 var tool_el = this.wrapper.createChild();
45879 this.toolbar = Roo.factory(config.toolbar);
45881 if (config.toolbar.items) {
45882 ti = config.toolbar.items ;
45883 delete config.toolbar.items ;
45887 this.toolbar.render(tool_el);
45888 for(var i =0;i < ti.length;i++) {
45889 // Roo.log(['add child', items[i]]);
45890 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45892 this.toolbar.items = nitems;
45894 delete config.toolbar;
45897 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45898 config.grid.scrollBody = true;;
45899 config.grid.monitorWindowResize = false; // turn off autosizing
45900 config.grid.autoHeight = false;
45901 config.grid.autoWidth = false;
45903 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45905 if (config.background) {
45906 // render grid on panel activation (if panel background)
45907 this.on('activate', function(gp) {
45908 if (!gp.grid.rendered) {
45909 gp.grid.render(this.wrapper);
45910 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45915 this.grid.render(this.wrapper);
45916 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45919 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45920 // ??? needed ??? config.el = this.wrapper;
45925 // xtype created footer. - not sure if will work as we normally have to render first..
45926 if (this.footer && !this.footer.el && this.footer.xtype) {
45928 var ctr = this.grid.getView().getFooterPanel(true);
45929 this.footer.dataSource = this.grid.dataSource;
45930 this.footer = Roo.factory(this.footer, Roo);
45931 this.footer.render(ctr);
45941 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45944 getId : function(){
45945 return this.grid.id;
45949 * Returns the grid for this panel
45950 * @return {Roo.bootstrap.Table}
45952 getGrid : function(){
45956 setSize : function(width, height)
45959 //if(!this.ignoreResize(width, height)){
45960 var grid = this.grid;
45961 var size = this.adjustForComponents(width, height);
45962 // tfoot is not a footer?
45965 var gridel = grid.getGridEl();
45966 gridel.setSize(size.width, size.height);
45968 var tbd = grid.getGridEl().select('tbody', true).first();
45969 var thd = grid.getGridEl().select('thead',true).first();
45970 var tbf= grid.getGridEl().select('tfoot', true).first();
45973 size.height -= tbf.getHeight();
45976 size.height -= thd.getHeight();
45979 tbd.setSize(size.width, size.height );
45980 // this is for the account management tab -seems to work there.
45981 var thd = grid.getGridEl().select('thead',true).first();
45983 // tbd.setSize(size.width, size.height - thd.getHeight());
45993 beforeSlide : function(){
45994 this.grid.getView().scroller.clip();
45997 afterSlide : function(){
45998 this.grid.getView().scroller.unclip();
46001 destroy : function(){
46002 this.grid.destroy();
46004 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
46009 * @class Roo.bootstrap.panel.Nest
46010 * @extends Roo.bootstrap.panel.Content
46012 * Create a new Panel, that can contain a layout.Border.
46015 * @param {String/Object} config A string to set only the title or a config object
46017 Roo.bootstrap.panel.Nest = function(config)
46019 // construct with only one argument..
46020 /* FIXME - implement nicer consturctors
46021 if (layout.layout) {
46023 layout = config.layout;
46024 delete config.layout;
46026 if (layout.xtype && !layout.getEl) {
46027 // then layout needs constructing..
46028 layout = Roo.factory(layout, Roo);
46032 config.el = config.layout.getEl();
46034 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46036 config.layout.monitorWindowResize = false; // turn off autosizing
46037 this.layout = config.layout;
46038 this.layout.getEl().addClass("roo-layout-nested-layout");
46039 this.layout.parent = this;
46046 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46048 * @cfg {Roo.BorderLayout} layout The layout for this panel
46052 setSize : function(width, height){
46053 if(!this.ignoreResize(width, height)){
46054 var size = this.adjustForComponents(width, height);
46055 var el = this.layout.getEl();
46056 if (size.height < 1) {
46057 el.setWidth(size.width);
46059 el.setSize(size.width, size.height);
46061 var touch = el.dom.offsetWidth;
46062 this.layout.layout();
46063 // ie requires a double layout on the first pass
46064 if(Roo.isIE && !this.initialized){
46065 this.initialized = true;
46066 this.layout.layout();
46071 // activate all subpanels if not currently active..
46073 setActiveState : function(active){
46074 this.active = active;
46075 this.setActiveClass(active);
46078 this.fireEvent("deactivate", this);
46082 this.fireEvent("activate", this);
46083 // not sure if this should happen before or after..
46084 if (!this.layout) {
46085 return; // should not happen..
46088 for (var r in this.layout.regions) {
46089 reg = this.layout.getRegion(r);
46090 if (reg.getActivePanel()) {
46091 //reg.showPanel(reg.getActivePanel()); // force it to activate..
46092 reg.setActivePanel(reg.getActivePanel());
46095 if (!reg.panels.length) {
46098 reg.showPanel(reg.getPanel(0));
46107 * Returns the nested BorderLayout for this panel
46108 * @return {Roo.BorderLayout}
46110 getLayout : function(){
46111 return this.layout;
46115 * Adds a xtype elements to the layout of the nested panel
46119 xtype : 'ContentPanel',
46126 xtype : 'NestedLayoutPanel',
46132 items : [ ... list of content panels or nested layout panels.. ]
46136 * @param {Object} cfg Xtype definition of item to add.
46138 addxtype : function(cfg) {
46139 return this.layout.addxtype(cfg);
46144 * Ext JS Library 1.1.1
46145 * Copyright(c) 2006-2007, Ext JS, LLC.
46147 * Originally Released Under LGPL - original licence link has changed is not relivant.
46150 * <script type="text/javascript">
46153 * @class Roo.TabPanel
46154 * @extends Roo.util.Observable
46155 * A lightweight tab container.
46159 // basic tabs 1, built from existing content
46160 var tabs = new Roo.TabPanel("tabs1");
46161 tabs.addTab("script", "View Script");
46162 tabs.addTab("markup", "View Markup");
46163 tabs.activate("script");
46165 // more advanced tabs, built from javascript
46166 var jtabs = new Roo.TabPanel("jtabs");
46167 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46169 // set up the UpdateManager
46170 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46171 var updater = tab2.getUpdateManager();
46172 updater.setDefaultUrl("ajax1.htm");
46173 tab2.on('activate', updater.refresh, updater, true);
46175 // Use setUrl for Ajax loading
46176 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46177 tab3.setUrl("ajax2.htm", null, true);
46180 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46183 jtabs.activate("jtabs-1");
46186 * Create a new TabPanel.
46187 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46188 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46190 Roo.bootstrap.panel.Tabs = function(config){
46192 * The container element for this TabPanel.
46193 * @type Roo.Element
46195 this.el = Roo.get(config.el);
46198 if(typeof config == "boolean"){
46199 this.tabPosition = config ? "bottom" : "top";
46201 Roo.apply(this, config);
46205 if(this.tabPosition == "bottom"){
46206 // if tabs are at the bottom = create the body first.
46207 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46208 this.el.addClass("roo-tabs-bottom");
46210 // next create the tabs holders
46212 if (this.tabPosition == "west"){
46214 var reg = this.region; // fake it..
46216 if (!reg.mgr.parent) {
46219 reg = reg.mgr.parent.region;
46221 Roo.log("got nest?");
46223 if (reg.mgr.getRegion('west')) {
46224 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46225 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46226 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46227 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46228 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46236 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46237 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46238 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46239 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46244 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46247 // finally - if tabs are at the top, then create the body last..
46248 if(this.tabPosition != "bottom"){
46249 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46250 * @type Roo.Element
46252 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46253 this.el.addClass("roo-tabs-top");
46257 this.bodyEl.setStyle("position", "relative");
46259 this.active = null;
46260 this.activateDelegate = this.activate.createDelegate(this);
46265 * Fires when the active tab changes
46266 * @param {Roo.TabPanel} this
46267 * @param {Roo.TabPanelItem} activePanel The new active tab
46271 * @event beforetabchange
46272 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46273 * @param {Roo.TabPanel} this
46274 * @param {Object} e Set cancel to true on this object to cancel the tab change
46275 * @param {Roo.TabPanelItem} tab The tab being changed to
46277 "beforetabchange" : true
46280 Roo.EventManager.onWindowResize(this.onResize, this);
46281 this.cpad = this.el.getPadding("lr");
46282 this.hiddenCount = 0;
46285 // toolbar on the tabbar support...
46286 if (this.toolbar) {
46287 alert("no toolbar support yet");
46288 this.toolbar = false;
46290 var tcfg = this.toolbar;
46291 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
46292 this.toolbar = new Roo.Toolbar(tcfg);
46293 if (Roo.isSafari) {
46294 var tbl = tcfg.container.child('table', true);
46295 tbl.setAttribute('width', '100%');
46303 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46306 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46308 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46310 tabPosition : "top",
46312 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46314 currentTabWidth : 0,
46316 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46320 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46324 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46326 preferredTabWidth : 175,
46328 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46330 resizeTabs : false,
46332 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46334 monitorResize : true,
46336 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
46338 toolbar : false, // set by caller..
46340 region : false, /// set by caller
46342 disableTooltips : true, // not used yet...
46345 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46346 * @param {String} id The id of the div to use <b>or create</b>
46347 * @param {String} text The text for the tab
46348 * @param {String} content (optional) Content to put in the TabPanelItem body
46349 * @param {Boolean} closable (optional) True to create a close icon on the tab
46350 * @return {Roo.TabPanelItem} The created TabPanelItem
46352 addTab : function(id, text, content, closable, tpl)
46354 var item = new Roo.bootstrap.panel.TabItem({
46358 closable : closable,
46361 this.addTabItem(item);
46363 item.setContent(content);
46369 * Returns the {@link Roo.TabPanelItem} with the specified id/index
46370 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46371 * @return {Roo.TabPanelItem}
46373 getTab : function(id){
46374 return this.items[id];
46378 * Hides the {@link Roo.TabPanelItem} with the specified id/index
46379 * @param {String/Number} id The id or index of the TabPanelItem to hide.
46381 hideTab : function(id){
46382 var t = this.items[id];
46385 this.hiddenCount++;
46386 this.autoSizeTabs();
46391 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46392 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46394 unhideTab : function(id){
46395 var t = this.items[id];
46397 t.setHidden(false);
46398 this.hiddenCount--;
46399 this.autoSizeTabs();
46404 * Adds an existing {@link Roo.TabPanelItem}.
46405 * @param {Roo.TabPanelItem} item The TabPanelItem to add
46407 addTabItem : function(item)
46409 this.items[item.id] = item;
46410 this.items.push(item);
46411 this.autoSizeTabs();
46412 // if(this.resizeTabs){
46413 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46414 // this.autoSizeTabs();
46416 // item.autoSize();
46421 * Removes a {@link Roo.TabPanelItem}.
46422 * @param {String/Number} id The id or index of the TabPanelItem to remove.
46424 removeTab : function(id){
46425 var items = this.items;
46426 var tab = items[id];
46427 if(!tab) { return; }
46428 var index = items.indexOf(tab);
46429 if(this.active == tab && items.length > 1){
46430 var newTab = this.getNextAvailable(index);
46435 this.stripEl.dom.removeChild(tab.pnode.dom);
46436 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46437 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46439 items.splice(index, 1);
46440 delete this.items[tab.id];
46441 tab.fireEvent("close", tab);
46442 tab.purgeListeners();
46443 this.autoSizeTabs();
46446 getNextAvailable : function(start){
46447 var items = this.items;
46449 // look for a next tab that will slide over to
46450 // replace the one being removed
46451 while(index < items.length){
46452 var item = items[++index];
46453 if(item && !item.isHidden()){
46457 // if one isn't found select the previous tab (on the left)
46460 var item = items[--index];
46461 if(item && !item.isHidden()){
46469 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46470 * @param {String/Number} id The id or index of the TabPanelItem to disable.
46472 disableTab : function(id){
46473 var tab = this.items[id];
46474 if(tab && this.active != tab){
46480 * Enables a {@link Roo.TabPanelItem} that is disabled.
46481 * @param {String/Number} id The id or index of the TabPanelItem to enable.
46483 enableTab : function(id){
46484 var tab = this.items[id];
46489 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46490 * @param {String/Number} id The id or index of the TabPanelItem to activate.
46491 * @return {Roo.TabPanelItem} The TabPanelItem.
46493 activate : function(id)
46495 //Roo.log('activite:' + id);
46497 var tab = this.items[id];
46501 if(tab == this.active || tab.disabled){
46505 this.fireEvent("beforetabchange", this, e, tab);
46506 if(e.cancel !== true && !tab.disabled){
46508 this.active.hide();
46510 this.active = this.items[id];
46511 this.active.show();
46512 this.fireEvent("tabchange", this, this.active);
46518 * Gets the active {@link Roo.TabPanelItem}.
46519 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46521 getActiveTab : function(){
46522 return this.active;
46526 * Updates the tab body element to fit the height of the container element
46527 * for overflow scrolling
46528 * @param {Number} targetHeight (optional) Override the starting height from the elements height
46530 syncHeight : function(targetHeight){
46531 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46532 var bm = this.bodyEl.getMargins();
46533 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46534 this.bodyEl.setHeight(newHeight);
46538 onResize : function(){
46539 if(this.monitorResize){
46540 this.autoSizeTabs();
46545 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46547 beginUpdate : function(){
46548 this.updating = true;
46552 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46554 endUpdate : function(){
46555 this.updating = false;
46556 this.autoSizeTabs();
46560 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46562 autoSizeTabs : function()
46564 var count = this.items.length;
46565 var vcount = count - this.hiddenCount;
46568 this.stripEl.hide();
46570 this.stripEl.show();
46573 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46578 var w = Math.max(this.el.getWidth() - this.cpad, 10);
46579 var availWidth = Math.floor(w / vcount);
46580 var b = this.stripBody;
46581 if(b.getWidth() > w){
46582 var tabs = this.items;
46583 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46584 if(availWidth < this.minTabWidth){
46585 /*if(!this.sleft){ // incomplete scrolling code
46586 this.createScrollButtons();
46589 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46592 if(this.currentTabWidth < this.preferredTabWidth){
46593 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46599 * Returns the number of tabs in this TabPanel.
46602 getCount : function(){
46603 return this.items.length;
46607 * Resizes all the tabs to the passed width
46608 * @param {Number} The new width
46610 setTabWidth : function(width){
46611 this.currentTabWidth = width;
46612 for(var i = 0, len = this.items.length; i < len; i++) {
46613 if(!this.items[i].isHidden()) {
46614 this.items[i].setWidth(width);
46620 * Destroys this TabPanel
46621 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46623 destroy : function(removeEl){
46624 Roo.EventManager.removeResizeListener(this.onResize, this);
46625 for(var i = 0, len = this.items.length; i < len; i++){
46626 this.items[i].purgeListeners();
46628 if(removeEl === true){
46629 this.el.update("");
46634 createStrip : function(container)
46636 var strip = document.createElement("nav");
46637 strip.className = Roo.bootstrap.version == 4 ?
46638 "navbar-light bg-light" :
46639 "navbar navbar-default"; //"x-tabs-wrap";
46640 container.appendChild(strip);
46644 createStripList : function(strip)
46646 // div wrapper for retard IE
46647 // returns the "tr" element.
46648 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46649 //'<div class="x-tabs-strip-wrap">'+
46650 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46651 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46652 return strip.firstChild; //.firstChild.firstChild.firstChild;
46654 createBody : function(container)
46656 var body = document.createElement("div");
46657 Roo.id(body, "tab-body");
46658 //Roo.fly(body).addClass("x-tabs-body");
46659 Roo.fly(body).addClass("tab-content");
46660 container.appendChild(body);
46663 createItemBody :function(bodyEl, id){
46664 var body = Roo.getDom(id);
46666 body = document.createElement("div");
46669 //Roo.fly(body).addClass("x-tabs-item-body");
46670 Roo.fly(body).addClass("tab-pane");
46671 bodyEl.insertBefore(body, bodyEl.firstChild);
46675 createStripElements : function(stripEl, text, closable, tpl)
46677 var td = document.createElement("li"); // was td..
46678 td.className = 'nav-item';
46680 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46683 stripEl.appendChild(td);
46685 td.className = "x-tabs-closable";
46686 if(!this.closeTpl){
46687 this.closeTpl = new Roo.Template(
46688 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46689 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46690 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
46693 var el = this.closeTpl.overwrite(td, {"text": text});
46694 var close = el.getElementsByTagName("div")[0];
46695 var inner = el.getElementsByTagName("em")[0];
46696 return {"el": el, "close": close, "inner": inner};
46699 // not sure what this is..
46700 // if(!this.tabTpl){
46701 //this.tabTpl = new Roo.Template(
46702 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46703 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46705 // this.tabTpl = new Roo.Template(
46706 // '<a href="#">' +
46707 // '<span unselectable="on"' +
46708 // (this.disableTooltips ? '' : ' title="{text}"') +
46709 // ' >{text}</span></a>'
46715 var template = tpl || this.tabTpl || false;
46718 template = new Roo.Template(
46719 Roo.bootstrap.version == 4 ?
46721 '<a class="nav-link" href="#" unselectable="on"' +
46722 (this.disableTooltips ? '' : ' title="{text}"') +
46725 '<a class="nav-link" href="#">' +
46726 '<span unselectable="on"' +
46727 (this.disableTooltips ? '' : ' title="{text}"') +
46728 ' >{text}</span></a>'
46733 switch (typeof(template)) {
46737 template = new Roo.Template(template);
46743 var el = template.overwrite(td, {"text": text});
46745 var inner = el.getElementsByTagName("span")[0];
46747 return {"el": el, "inner": inner};
46755 * @class Roo.TabPanelItem
46756 * @extends Roo.util.Observable
46757 * Represents an individual item (tab plus body) in a TabPanel.
46758 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46759 * @param {String} id The id of this TabPanelItem
46760 * @param {String} text The text for the tab of this TabPanelItem
46761 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46763 Roo.bootstrap.panel.TabItem = function(config){
46765 * The {@link Roo.TabPanel} this TabPanelItem belongs to
46766 * @type Roo.TabPanel
46768 this.tabPanel = config.panel;
46770 * The id for this TabPanelItem
46773 this.id = config.id;
46775 this.disabled = false;
46777 this.text = config.text;
46779 this.loaded = false;
46780 this.closable = config.closable;
46783 * The body element for this TabPanelItem.
46784 * @type Roo.Element
46786 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46787 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46788 this.bodyEl.setStyle("display", "block");
46789 this.bodyEl.setStyle("zoom", "1");
46790 //this.hideAction();
46792 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46794 this.el = Roo.get(els.el);
46795 this.inner = Roo.get(els.inner, true);
46796 this.textEl = Roo.bootstrap.version == 4 ?
46797 this.el : Roo.get(this.el.dom.firstChild, true);
46799 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46800 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46803 // this.el.on("mousedown", this.onTabMouseDown, this);
46804 this.el.on("click", this.onTabClick, this);
46806 if(config.closable){
46807 var c = Roo.get(els.close, true);
46808 c.dom.title = this.closeText;
46809 c.addClassOnOver("close-over");
46810 c.on("click", this.closeClick, this);
46816 * Fires when this tab becomes the active tab.
46817 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46818 * @param {Roo.TabPanelItem} this
46822 * @event beforeclose
46823 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46824 * @param {Roo.TabPanelItem} this
46825 * @param {Object} e Set cancel to true on this object to cancel the close.
46827 "beforeclose": true,
46830 * Fires when this tab is closed.
46831 * @param {Roo.TabPanelItem} this
46835 * @event deactivate
46836 * Fires when this tab is no longer the active tab.
46837 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46838 * @param {Roo.TabPanelItem} this
46840 "deactivate" : true
46842 this.hidden = false;
46844 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46847 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46849 purgeListeners : function(){
46850 Roo.util.Observable.prototype.purgeListeners.call(this);
46851 this.el.removeAllListeners();
46854 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46857 this.status_node.addClass("active");
46860 this.tabPanel.stripWrap.repaint();
46862 this.fireEvent("activate", this.tabPanel, this);
46866 * Returns true if this tab is the active tab.
46867 * @return {Boolean}
46869 isActive : function(){
46870 return this.tabPanel.getActiveTab() == this;
46874 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46877 this.status_node.removeClass("active");
46879 this.fireEvent("deactivate", this.tabPanel, this);
46882 hideAction : function(){
46883 this.bodyEl.hide();
46884 this.bodyEl.setStyle("position", "absolute");
46885 this.bodyEl.setLeft("-20000px");
46886 this.bodyEl.setTop("-20000px");
46889 showAction : function(){
46890 this.bodyEl.setStyle("position", "relative");
46891 this.bodyEl.setTop("");
46892 this.bodyEl.setLeft("");
46893 this.bodyEl.show();
46897 * Set the tooltip for the tab.
46898 * @param {String} tooltip The tab's tooltip
46900 setTooltip : function(text){
46901 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46902 this.textEl.dom.qtip = text;
46903 this.textEl.dom.removeAttribute('title');
46905 this.textEl.dom.title = text;
46909 onTabClick : function(e){
46910 e.preventDefault();
46911 this.tabPanel.activate(this.id);
46914 onTabMouseDown : function(e){
46915 e.preventDefault();
46916 this.tabPanel.activate(this.id);
46919 getWidth : function(){
46920 return this.inner.getWidth();
46923 setWidth : function(width){
46924 var iwidth = width - this.linode.getPadding("lr");
46925 this.inner.setWidth(iwidth);
46926 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46927 this.linode.setWidth(width);
46931 * Show or hide the tab
46932 * @param {Boolean} hidden True to hide or false to show.
46934 setHidden : function(hidden){
46935 this.hidden = hidden;
46936 this.linode.setStyle("display", hidden ? "none" : "");
46940 * Returns true if this tab is "hidden"
46941 * @return {Boolean}
46943 isHidden : function(){
46944 return this.hidden;
46948 * Returns the text for this tab
46951 getText : function(){
46955 autoSize : function(){
46956 //this.el.beginMeasure();
46957 this.textEl.setWidth(1);
46959 * #2804 [new] Tabs in Roojs
46960 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46962 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46963 //this.el.endMeasure();
46967 * Sets the text for the tab (Note: this also sets the tooltip text)
46968 * @param {String} text The tab's text and tooltip
46970 setText : function(text){
46972 this.textEl.update(text);
46973 this.setTooltip(text);
46974 //if(!this.tabPanel.resizeTabs){
46975 // this.autoSize();
46979 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46981 activate : function(){
46982 this.tabPanel.activate(this.id);
46986 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46988 disable : function(){
46989 if(this.tabPanel.active != this){
46990 this.disabled = true;
46991 this.status_node.addClass("disabled");
46996 * Enables this TabPanelItem if it was previously disabled.
46998 enable : function(){
46999 this.disabled = false;
47000 this.status_node.removeClass("disabled");
47004 * Sets the content for this TabPanelItem.
47005 * @param {String} content The content
47006 * @param {Boolean} loadScripts true to look for and load scripts
47008 setContent : function(content, loadScripts){
47009 this.bodyEl.update(content, loadScripts);
47013 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47014 * @return {Roo.UpdateManager} The UpdateManager
47016 getUpdateManager : function(){
47017 return this.bodyEl.getUpdateManager();
47021 * Set a URL to be used to load the content for this TabPanelItem.
47022 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47023 * @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)
47024 * @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)
47025 * @return {Roo.UpdateManager} The UpdateManager
47027 setUrl : function(url, params, loadOnce){
47028 if(this.refreshDelegate){
47029 this.un('activate', this.refreshDelegate);
47031 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47032 this.on("activate", this.refreshDelegate);
47033 return this.bodyEl.getUpdateManager();
47037 _handleRefresh : function(url, params, loadOnce){
47038 if(!loadOnce || !this.loaded){
47039 var updater = this.bodyEl.getUpdateManager();
47040 updater.update(url, params, this._setLoaded.createDelegate(this));
47045 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
47046 * Will fail silently if the setUrl method has not been called.
47047 * This does not activate the panel, just updates its content.
47049 refresh : function(){
47050 if(this.refreshDelegate){
47051 this.loaded = false;
47052 this.refreshDelegate();
47057 _setLoaded : function(){
47058 this.loaded = true;
47062 closeClick : function(e){
47065 this.fireEvent("beforeclose", this, o);
47066 if(o.cancel !== true){
47067 this.tabPanel.removeTab(this.id);
47071 * The text displayed in the tooltip for the close icon.
47074 closeText : "Close this tab"
47077 * This script refer to:
47078 * Title: International Telephone Input
47079 * Author: Jack O'Connor
47080 * Code version: v12.1.12
47081 * Availability: https://github.com/jackocnr/intl-tel-input.git
47084 Roo.bootstrap.form.PhoneInputData = function() {
47087 "Afghanistan (افغانستان)",
47092 "Albania (Shqipëri)",
47097 "Algeria (الجزائر)",
47122 "Antigua and Barbuda",
47132 "Armenia (Հայաստան)",
47148 "Austria (Österreich)",
47153 "Azerbaijan (Azərbaycan)",
47163 "Bahrain (البحرين)",
47168 "Bangladesh (বাংলাদেশ)",
47178 "Belarus (Беларусь)",
47183 "Belgium (België)",
47213 "Bosnia and Herzegovina (Босна и Херцеговина)",
47228 "British Indian Ocean Territory",
47233 "British Virgin Islands",
47243 "Bulgaria (България)",
47253 "Burundi (Uburundi)",
47258 "Cambodia (កម្ពុជា)",
47263 "Cameroon (Cameroun)",
47272 ["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"]
47275 "Cape Verde (Kabu Verdi)",
47280 "Caribbean Netherlands",
47291 "Central African Republic (République centrafricaine)",
47311 "Christmas Island",
47317 "Cocos (Keeling) Islands",
47328 "Comoros (جزر القمر)",
47333 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47338 "Congo (Republic) (Congo-Brazzaville)",
47358 "Croatia (Hrvatska)",
47379 "Czech Republic (Česká republika)",
47384 "Denmark (Danmark)",
47399 "Dominican Republic (República Dominicana)",
47403 ["809", "829", "849"]
47421 "Equatorial Guinea (Guinea Ecuatorial)",
47441 "Falkland Islands (Islas Malvinas)",
47446 "Faroe Islands (Føroyar)",
47467 "French Guiana (Guyane française)",
47472 "French Polynesia (Polynésie française)",
47487 "Georgia (საქართველო)",
47492 "Germany (Deutschland)",
47512 "Greenland (Kalaallit Nunaat)",
47549 "Guinea-Bissau (Guiné Bissau)",
47574 "Hungary (Magyarország)",
47579 "Iceland (Ísland)",
47599 "Iraq (العراق)",
47615 "Israel (ישראל)",
47642 "Jordan (الأردن)",
47647 "Kazakhstan (Казахстан)",
47668 "Kuwait (الكويت)",
47673 "Kyrgyzstan (Кыргызстан)",
47683 "Latvia (Latvija)",
47688 "Lebanon (لبنان)",
47703 "Libya (ليبيا)",
47713 "Lithuania (Lietuva)",
47728 "Macedonia (FYROM) (Македонија)",
47733 "Madagascar (Madagasikara)",
47763 "Marshall Islands",
47773 "Mauritania (موريتانيا)",
47778 "Mauritius (Moris)",
47799 "Moldova (Republica Moldova)",
47809 "Mongolia (Монгол)",
47814 "Montenegro (Crna Gora)",
47824 "Morocco (المغرب)",
47830 "Mozambique (Moçambique)",
47835 "Myanmar (Burma) (မြန်မာ)",
47840 "Namibia (Namibië)",
47855 "Netherlands (Nederland)",
47860 "New Caledonia (Nouvelle-Calédonie)",
47895 "North Korea (조선 민주주의 인민 공화국)",
47900 "Northern Mariana Islands",
47916 "Pakistan (پاکستان)",
47926 "Palestine (فلسطين)",
47936 "Papua New Guinea",
47978 "Réunion (La Réunion)",
47984 "Romania (România)",
48000 "Saint Barthélemy",
48011 "Saint Kitts and Nevis",
48021 "Saint Martin (Saint-Martin (partie française))",
48027 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48032 "Saint Vincent and the Grenadines",
48047 "São Tomé and Príncipe (São Tomé e Príncipe)",
48052 "Saudi Arabia (المملكة العربية السعودية)",
48057 "Senegal (Sénégal)",
48087 "Slovakia (Slovensko)",
48092 "Slovenia (Slovenija)",
48102 "Somalia (Soomaaliya)",
48112 "South Korea (대한민국)",
48117 "South Sudan (جنوب السودان)",
48127 "Sri Lanka (ශ්රී ලංකාව)",
48132 "Sudan (السودان)",
48142 "Svalbard and Jan Mayen",
48153 "Sweden (Sverige)",
48158 "Switzerland (Schweiz)",
48163 "Syria (سوريا)",
48208 "Trinidad and Tobago",
48213 "Tunisia (تونس)",
48218 "Turkey (Türkiye)",
48228 "Turks and Caicos Islands",
48238 "U.S. Virgin Islands",
48248 "Ukraine (Україна)",
48253 "United Arab Emirates (الإمارات العربية المتحدة)",
48275 "Uzbekistan (Oʻzbekiston)",
48285 "Vatican City (Città del Vaticano)",
48296 "Vietnam (Việt Nam)",
48301 "Wallis and Futuna (Wallis-et-Futuna)",
48306 "Western Sahara (الصحراء الغربية)",
48312 "Yemen (اليمن)",
48336 * This script refer to:
48337 * Title: International Telephone Input
48338 * Author: Jack O'Connor
48339 * Code version: v12.1.12
48340 * Availability: https://github.com/jackocnr/intl-tel-input.git
48344 * @class Roo.bootstrap.form.PhoneInput
48345 * @extends Roo.bootstrap.form.TriggerField
48346 * An input with International dial-code selection
48348 * @cfg {String} defaultDialCode default '+852'
48349 * @cfg {Array} preferedCountries default []
48352 * Create a new PhoneInput.
48353 * @param {Object} config Configuration options
48356 Roo.bootstrap.form.PhoneInput = function(config) {
48357 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48360 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48362 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48364 listWidth: undefined,
48366 selectedClass: 'active',
48368 invalidClass : "has-warning",
48370 validClass: 'has-success',
48372 allowed: '0123456789',
48377 * @cfg {String} defaultDialCode The default dial code when initializing the input
48379 defaultDialCode: '+852',
48382 * @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
48384 preferedCountries: false,
48386 getAutoCreate : function()
48388 var data = Roo.bootstrap.form.PhoneInputData();
48389 var align = this.labelAlign || this.parentLabelAlign();
48392 this.allCountries = [];
48393 this.dialCodeMapping = [];
48395 for (var i = 0; i < data.length; i++) {
48397 this.allCountries[i] = {
48401 priority: c[3] || 0,
48402 areaCodes: c[4] || null
48404 this.dialCodeMapping[c[2]] = {
48407 priority: c[3] || 0,
48408 areaCodes: c[4] || null
48420 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48421 maxlength: this.max_length,
48422 cls : 'form-control tel-input',
48423 autocomplete: 'new-password'
48426 var hiddenInput = {
48429 cls: 'hidden-tel-input'
48433 hiddenInput.name = this.name;
48436 if (this.disabled) {
48437 input.disabled = true;
48440 var flag_container = {
48457 cls: this.hasFeedback ? 'has-feedback' : '',
48463 cls: 'dial-code-holder',
48470 cls: 'roo-select2-container input-group',
48477 if (this.fieldLabel.length) {
48480 tooltip: 'This field is required'
48486 cls: 'control-label',
48492 html: this.fieldLabel
48495 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48501 if(this.indicatorpos == 'right') {
48502 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48509 if(align == 'left') {
48517 if(this.labelWidth > 12){
48518 label.style = "width: " + this.labelWidth + 'px';
48520 if(this.labelWidth < 13 && this.labelmd == 0){
48521 this.labelmd = this.labelWidth;
48523 if(this.labellg > 0){
48524 label.cls += ' col-lg-' + this.labellg;
48525 input.cls += ' col-lg-' + (12 - this.labellg);
48527 if(this.labelmd > 0){
48528 label.cls += ' col-md-' + this.labelmd;
48529 container.cls += ' col-md-' + (12 - this.labelmd);
48531 if(this.labelsm > 0){
48532 label.cls += ' col-sm-' + this.labelsm;
48533 container.cls += ' col-sm-' + (12 - this.labelsm);
48535 if(this.labelxs > 0){
48536 label.cls += ' col-xs-' + this.labelxs;
48537 container.cls += ' col-xs-' + (12 - this.labelxs);
48547 var settings = this;
48549 ['xs','sm','md','lg'].map(function(size){
48550 if (settings[size]) {
48551 cfg.cls += ' col-' + size + '-' + settings[size];
48555 this.store = new Roo.data.Store({
48556 proxy : new Roo.data.MemoryProxy({}),
48557 reader : new Roo.data.JsonReader({
48568 'name' : 'dialCode',
48572 'name' : 'priority',
48576 'name' : 'areaCodes',
48583 if(!this.preferedCountries) {
48584 this.preferedCountries = [
48591 var p = this.preferedCountries.reverse();
48594 for (var i = 0; i < p.length; i++) {
48595 for (var j = 0; j < this.allCountries.length; j++) {
48596 if(this.allCountries[j].iso2 == p[i]) {
48597 var t = this.allCountries[j];
48598 this.allCountries.splice(j,1);
48599 this.allCountries.unshift(t);
48605 this.store.proxy.data = {
48607 data: this.allCountries
48613 initEvents : function()
48616 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48618 this.indicator = this.indicatorEl();
48619 this.flag = this.flagEl();
48620 this.dialCodeHolder = this.dialCodeHolderEl();
48622 this.trigger = this.el.select('div.flag-box',true).first();
48623 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48628 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48629 _this.list.setWidth(lw);
48632 this.list.on('mouseover', this.onViewOver, this);
48633 this.list.on('mousemove', this.onViewMove, this);
48634 this.inputEl().on("keyup", this.onKeyUp, this);
48635 this.inputEl().on("keypress", this.onKeyPress, this);
48637 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48639 this.view = new Roo.View(this.list, this.tpl, {
48640 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48643 this.view.on('click', this.onViewClick, this);
48644 this.setValue(this.defaultDialCode);
48647 onTriggerClick : function(e)
48649 Roo.log('trigger click');
48654 if(this.isExpanded()){
48656 this.hasFocus = false;
48658 this.store.load({});
48659 this.hasFocus = true;
48664 isExpanded : function()
48666 return this.list.isVisible();
48669 collapse : function()
48671 if(!this.isExpanded()){
48675 Roo.get(document).un('mousedown', this.collapseIf, this);
48676 Roo.get(document).un('mousewheel', this.collapseIf, this);
48677 this.fireEvent('collapse', this);
48681 expand : function()
48685 if(this.isExpanded() || !this.hasFocus){
48689 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48690 this.list.setWidth(lw);
48693 this.restrictHeight();
48695 Roo.get(document).on('mousedown', this.collapseIf, this);
48696 Roo.get(document).on('mousewheel', this.collapseIf, this);
48698 this.fireEvent('expand', this);
48701 restrictHeight : function()
48703 this.list.alignTo(this.inputEl(), this.listAlign);
48704 this.list.alignTo(this.inputEl(), this.listAlign);
48707 onViewOver : function(e, t)
48709 if(this.inKeyMode){
48712 var item = this.view.findItemFromChild(t);
48715 var index = this.view.indexOf(item);
48716 this.select(index, false);
48721 onViewClick : function(view, doFocus, el, e)
48723 var index = this.view.getSelectedIndexes()[0];
48725 var r = this.store.getAt(index);
48728 this.onSelect(r, index);
48730 if(doFocus !== false && !this.blockFocus){
48731 this.inputEl().focus();
48735 onViewMove : function(e, t)
48737 this.inKeyMode = false;
48740 select : function(index, scrollIntoView)
48742 this.selectedIndex = index;
48743 this.view.select(index);
48744 if(scrollIntoView !== false){
48745 var el = this.view.getNode(index);
48747 this.list.scrollChildIntoView(el, false);
48752 createList : function()
48754 this.list = Roo.get(document.body).createChild({
48756 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48757 style: 'display:none'
48760 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48763 collapseIf : function(e)
48765 var in_combo = e.within(this.el);
48766 var in_list = e.within(this.list);
48767 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48769 if (in_combo || in_list || is_list) {
48775 onSelect : function(record, index)
48777 if(this.fireEvent('beforeselect', this, record, index) !== false){
48779 this.setFlagClass(record.data.iso2);
48780 this.setDialCode(record.data.dialCode);
48781 this.hasFocus = false;
48783 this.fireEvent('select', this, record, index);
48787 flagEl : function()
48789 var flag = this.el.select('div.flag',true).first();
48796 dialCodeHolderEl : function()
48798 var d = this.el.select('input.dial-code-holder',true).first();
48805 setDialCode : function(v)
48807 this.dialCodeHolder.dom.value = '+'+v;
48810 setFlagClass : function(n)
48812 this.flag.dom.className = 'flag '+n;
48815 getValue : function()
48817 var v = this.inputEl().getValue();
48818 if(this.dialCodeHolder) {
48819 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48824 setValue : function(v)
48826 var d = this.getDialCode(v);
48828 //invalid dial code
48829 if(v.length == 0 || !d || d.length == 0) {
48831 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48832 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48838 this.setFlagClass(this.dialCodeMapping[d].iso2);
48839 this.setDialCode(d);
48840 this.inputEl().dom.value = v.replace('+'+d,'');
48841 this.hiddenEl().dom.value = this.getValue();
48846 getDialCode : function(v)
48850 if (v.length == 0) {
48851 return this.dialCodeHolder.dom.value;
48855 if (v.charAt(0) != "+") {
48858 var numericChars = "";
48859 for (var i = 1; i < v.length; i++) {
48860 var c = v.charAt(i);
48863 if (this.dialCodeMapping[numericChars]) {
48864 dialCode = v.substr(1, i);
48866 if (numericChars.length == 4) {
48876 this.setValue(this.defaultDialCode);
48880 hiddenEl : function()
48882 return this.el.select('input.hidden-tel-input',true).first();
48885 // after setting val
48886 onKeyUp : function(e){
48887 this.setValue(this.getValue());
48890 onKeyPress : function(e){
48891 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48898 * @class Roo.bootstrap.form.MoneyField
48899 * @extends Roo.bootstrap.form.ComboBox
48900 * Bootstrap MoneyField class
48903 * Create a new MoneyField.
48904 * @param {Object} config Configuration options
48907 Roo.bootstrap.form.MoneyField = function(config) {
48909 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48913 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48916 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48918 allowDecimals : true,
48920 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48922 decimalSeparator : ".",
48924 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48926 decimalPrecision : 0,
48928 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48930 allowNegative : true,
48932 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48936 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48938 minValue : Number.NEGATIVE_INFINITY,
48940 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48942 maxValue : Number.MAX_VALUE,
48944 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48946 minText : "The minimum value for this field is {0}",
48948 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48950 maxText : "The maximum value for this field is {0}",
48952 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48953 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48955 nanText : "{0} is not a valid number",
48957 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48961 * @cfg {String} defaults currency of the MoneyField
48962 * value should be in lkey
48964 defaultCurrency : false,
48966 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48968 thousandsDelimiter : false,
48970 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48979 * @cfg {Roo.data.Store} store Store to lookup currency??
48983 getAutoCreate : function()
48985 var align = this.labelAlign || this.parentLabelAlign();
48997 cls : 'form-control roo-money-amount-input',
48998 autocomplete: 'new-password'
49001 var hiddenInput = {
49005 cls: 'hidden-number-input'
49008 if(this.max_length) {
49009 input.maxlength = this.max_length;
49013 hiddenInput.name = this.name;
49016 if (this.disabled) {
49017 input.disabled = true;
49020 var clg = 12 - this.inputlg;
49021 var cmd = 12 - this.inputmd;
49022 var csm = 12 - this.inputsm;
49023 var cxs = 12 - this.inputxs;
49027 cls : 'row roo-money-field',
49031 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49035 cls: 'roo-select2-container input-group',
49039 cls : 'form-control roo-money-currency-input',
49040 autocomplete: 'new-password',
49042 name : this.currencyName
49046 cls : 'input-group-addon',
49060 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49064 cls: this.hasFeedback ? 'has-feedback' : '',
49075 if (this.fieldLabel.length) {
49078 tooltip: 'This field is required'
49084 cls: 'control-label',
49090 html: this.fieldLabel
49093 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49099 if(this.indicatorpos == 'right') {
49100 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49107 if(align == 'left') {
49115 if(this.labelWidth > 12){
49116 label.style = "width: " + this.labelWidth + 'px';
49118 if(this.labelWidth < 13 && this.labelmd == 0){
49119 this.labelmd = this.labelWidth;
49121 if(this.labellg > 0){
49122 label.cls += ' col-lg-' + this.labellg;
49123 input.cls += ' col-lg-' + (12 - this.labellg);
49125 if(this.labelmd > 0){
49126 label.cls += ' col-md-' + this.labelmd;
49127 container.cls += ' col-md-' + (12 - this.labelmd);
49129 if(this.labelsm > 0){
49130 label.cls += ' col-sm-' + this.labelsm;
49131 container.cls += ' col-sm-' + (12 - this.labelsm);
49133 if(this.labelxs > 0){
49134 label.cls += ' col-xs-' + this.labelxs;
49135 container.cls += ' col-xs-' + (12 - this.labelxs);
49146 var settings = this;
49148 ['xs','sm','md','lg'].map(function(size){
49149 if (settings[size]) {
49150 cfg.cls += ' col-' + size + '-' + settings[size];
49157 initEvents : function()
49159 this.indicator = this.indicatorEl();
49161 this.initCurrencyEvent();
49163 this.initNumberEvent();
49166 initCurrencyEvent : function()
49169 throw "can not find store for combo";
49172 this.store = Roo.factory(this.store, Roo.data);
49173 this.store.parent = this;
49177 this.triggerEl = this.el.select('.input-group-addon', true).first();
49179 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49184 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49185 _this.list.setWidth(lw);
49188 this.list.on('mouseover', this.onViewOver, this);
49189 this.list.on('mousemove', this.onViewMove, this);
49190 this.list.on('scroll', this.onViewScroll, this);
49193 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49196 this.view = new Roo.View(this.list, this.tpl, {
49197 singleSelect:true, store: this.store, selectedClass: this.selectedClass
49200 this.view.on('click', this.onViewClick, this);
49202 this.store.on('beforeload', this.onBeforeLoad, this);
49203 this.store.on('load', this.onLoad, this);
49204 this.store.on('loadexception', this.onLoadException, this);
49206 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49207 "up" : function(e){
49208 this.inKeyMode = true;
49212 "down" : function(e){
49213 if(!this.isExpanded()){
49214 this.onTriggerClick();
49216 this.inKeyMode = true;
49221 "enter" : function(e){
49224 if(this.fireEvent("specialkey", this, e)){
49225 this.onViewClick(false);
49231 "esc" : function(e){
49235 "tab" : function(e){
49238 if(this.fireEvent("specialkey", this, e)){
49239 this.onViewClick(false);
49247 doRelay : function(foo, bar, hname){
49248 if(hname == 'down' || this.scope.isExpanded()){
49249 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49257 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49261 initNumberEvent : function(e)
49263 this.inputEl().on("keydown" , this.fireKey, this);
49264 this.inputEl().on("focus", this.onFocus, this);
49265 this.inputEl().on("blur", this.onBlur, this);
49267 this.inputEl().relayEvent('keyup', this);
49269 if(this.indicator){
49270 this.indicator.addClass('invisible');
49273 this.originalValue = this.getValue();
49275 if(this.validationEvent == 'keyup'){
49276 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49277 this.inputEl().on('keyup', this.filterValidation, this);
49279 else if(this.validationEvent !== false){
49280 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49283 if(this.selectOnFocus){
49284 this.on("focus", this.preFocus, this);
49287 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49288 this.inputEl().on("keypress", this.filterKeys, this);
49290 this.inputEl().relayEvent('keypress', this);
49293 var allowed = "0123456789";
49295 if(this.allowDecimals){
49296 allowed += this.decimalSeparator;
49299 if(this.allowNegative){
49303 if(this.thousandsDelimiter) {
49307 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49309 var keyPress = function(e){
49311 var k = e.getKey();
49313 var c = e.getCharCode();
49316 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49317 allowed.indexOf(String.fromCharCode(c)) === -1
49323 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49327 if(allowed.indexOf(String.fromCharCode(c)) === -1){
49332 this.inputEl().on("keypress", keyPress, this);
49336 onTriggerClick : function(e)
49343 this.loadNext = false;
49345 if(this.isExpanded()){
49350 this.hasFocus = true;
49352 if(this.triggerAction == 'all') {
49353 this.doQuery(this.allQuery, true);
49357 this.doQuery(this.getRawValue());
49360 getCurrency : function()
49362 var v = this.currencyEl().getValue();
49367 restrictHeight : function()
49369 this.list.alignTo(this.currencyEl(), this.listAlign);
49370 this.list.alignTo(this.currencyEl(), this.listAlign);
49373 onViewClick : function(view, doFocus, el, e)
49375 var index = this.view.getSelectedIndexes()[0];
49377 var r = this.store.getAt(index);
49380 this.onSelect(r, index);
49384 onSelect : function(record, index){
49386 if(this.fireEvent('beforeselect', this, record, index) !== false){
49388 this.setFromCurrencyData(index > -1 ? record.data : false);
49392 this.fireEvent('select', this, record, index);
49396 setFromCurrencyData : function(o)
49400 this.lastCurrency = o;
49402 if (this.currencyField) {
49403 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49405 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
49408 this.lastSelectionText = currency;
49410 //setting default currency
49411 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49412 this.setCurrency(this.defaultCurrency);
49416 this.setCurrency(currency);
49419 setFromData : function(o)
49423 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49425 this.setFromCurrencyData(c);
49430 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49432 Roo.log('no value set for '+ (this.name ? this.name : this.id));
49435 this.setValue(value);
49439 setCurrency : function(v)
49441 this.currencyValue = v;
49444 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49449 setValue : function(v)
49451 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49457 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49459 this.inputEl().dom.value = (v == '') ? '' :
49460 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49462 if(!this.allowZero && v === '0') {
49463 this.hiddenEl().dom.value = '';
49464 this.inputEl().dom.value = '';
49471 getRawValue : function()
49473 var v = this.inputEl().getValue();
49478 getValue : function()
49480 return this.fixPrecision(this.parseValue(this.getRawValue()));
49483 parseValue : function(value)
49485 if(this.thousandsDelimiter) {
49487 r = new RegExp(",", "g");
49488 value = value.replace(r, "");
49491 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49492 return isNaN(value) ? '' : value;
49496 fixPrecision : function(value)
49498 if(this.thousandsDelimiter) {
49500 r = new RegExp(",", "g");
49501 value = value.replace(r, "");
49504 var nan = isNaN(value);
49506 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49507 return nan ? '' : value;
49509 return parseFloat(value).toFixed(this.decimalPrecision);
49512 decimalPrecisionFcn : function(v)
49514 return Math.floor(v);
49517 validateValue : function(value)
49519 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49523 var num = this.parseValue(value);
49526 this.markInvalid(String.format(this.nanText, value));
49530 if(num < this.minValue){
49531 this.markInvalid(String.format(this.minText, this.minValue));
49535 if(num > this.maxValue){
49536 this.markInvalid(String.format(this.maxText, this.maxValue));
49543 validate : function()
49545 if(this.disabled || this.allowBlank){
49550 var currency = this.getCurrency();
49552 if(this.validateValue(this.getRawValue()) && currency.length){
49557 this.markInvalid();
49561 getName: function()
49566 beforeBlur : function()
49572 var v = this.parseValue(this.getRawValue());
49579 onBlur : function()
49583 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49584 //this.el.removeClass(this.focusClass);
49587 this.hasFocus = false;
49589 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49593 var v = this.getValue();
49595 if(String(v) !== String(this.startValue)){
49596 this.fireEvent('change', this, v, this.startValue);
49599 this.fireEvent("blur", this);
49602 inputEl : function()
49604 return this.el.select('.roo-money-amount-input', true).first();
49607 currencyEl : function()
49609 return this.el.select('.roo-money-currency-input', true).first();
49612 hiddenEl : function()
49614 return this.el.select('input.hidden-number-input',true).first();
49618 * @class Roo.bootstrap.BezierSignature
49619 * @extends Roo.bootstrap.Component
49620 * Bootstrap BezierSignature class
49621 * This script refer to:
49622 * Title: Signature Pad
49624 * Availability: https://github.com/szimek/signature_pad
49627 * Create a new BezierSignature
49628 * @param {Object} config The config object
49631 Roo.bootstrap.BezierSignature = function(config){
49632 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49638 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49645 mouse_btn_down: true,
49648 * @cfg {int} canvas height
49650 canvas_height: '200px',
49653 * @cfg {float|function} Radius of a single dot.
49658 * @cfg {float} Minimum width of a line. Defaults to 0.5.
49663 * @cfg {float} Maximum width of a line. Defaults to 2.5.
49668 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49673 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49678 * @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.
49680 bg_color: 'rgba(0, 0, 0, 0)',
49683 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49685 dot_color: 'black',
49688 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49690 velocity_filter_weight: 0.7,
49693 * @cfg {function} Callback when stroke begin.
49698 * @cfg {function} Callback when stroke end.
49702 getAutoCreate : function()
49704 var cls = 'roo-signature column';
49707 cls += ' ' + this.cls;
49717 for(var i = 0; i < col_sizes.length; i++) {
49718 if(this[col_sizes[i]]) {
49719 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49729 cls: 'roo-signature-body',
49733 cls: 'roo-signature-body-canvas',
49734 height: this.canvas_height,
49735 width: this.canvas_width
49742 style: 'display: none'
49750 initEvents: function()
49752 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49754 var canvas = this.canvasEl();
49756 // mouse && touch event swapping...
49757 canvas.dom.style.touchAction = 'none';
49758 canvas.dom.style.msTouchAction = 'none';
49760 this.mouse_btn_down = false;
49761 canvas.on('mousedown', this._handleMouseDown, this);
49762 canvas.on('mousemove', this._handleMouseMove, this);
49763 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49765 if (window.PointerEvent) {
49766 canvas.on('pointerdown', this._handleMouseDown, this);
49767 canvas.on('pointermove', this._handleMouseMove, this);
49768 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49771 if ('ontouchstart' in window) {
49772 canvas.on('touchstart', this._handleTouchStart, this);
49773 canvas.on('touchmove', this._handleTouchMove, this);
49774 canvas.on('touchend', this._handleTouchEnd, this);
49777 Roo.EventManager.onWindowResize(this.resize, this, true);
49779 // file input event
49780 this.fileEl().on('change', this.uploadImage, this);
49787 resize: function(){
49789 var canvas = this.canvasEl().dom;
49790 var ctx = this.canvasElCtx();
49791 var img_data = false;
49793 if(canvas.width > 0) {
49794 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49796 // setting canvas width will clean img data
49799 var style = window.getComputedStyle ?
49800 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49802 var padding_left = parseInt(style.paddingLeft) || 0;
49803 var padding_right = parseInt(style.paddingRight) || 0;
49805 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49808 ctx.putImageData(img_data, 0, 0);
49812 _handleMouseDown: function(e)
49814 if (e.browserEvent.which === 1) {
49815 this.mouse_btn_down = true;
49816 this.strokeBegin(e);
49820 _handleMouseMove: function (e)
49822 if (this.mouse_btn_down) {
49823 this.strokeMoveUpdate(e);
49827 _handleMouseUp: function (e)
49829 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49830 this.mouse_btn_down = false;
49835 _handleTouchStart: function (e) {
49837 e.preventDefault();
49838 if (e.browserEvent.targetTouches.length === 1) {
49839 // var touch = e.browserEvent.changedTouches[0];
49840 // this.strokeBegin(touch);
49842 this.strokeBegin(e); // assume e catching the correct xy...
49846 _handleTouchMove: function (e) {
49847 e.preventDefault();
49848 // var touch = event.targetTouches[0];
49849 // _this._strokeMoveUpdate(touch);
49850 this.strokeMoveUpdate(e);
49853 _handleTouchEnd: function (e) {
49854 var wasCanvasTouched = e.target === this.canvasEl().dom;
49855 if (wasCanvasTouched) {
49856 e.preventDefault();
49857 // var touch = event.changedTouches[0];
49858 // _this._strokeEnd(touch);
49863 reset: function () {
49864 this._lastPoints = [];
49865 this._lastVelocity = 0;
49866 this._lastWidth = (this.min_width + this.max_width) / 2;
49867 this.canvasElCtx().fillStyle = this.dot_color;
49870 strokeMoveUpdate: function(e)
49872 this.strokeUpdate(e);
49874 if (this.throttle) {
49875 this.throttleStroke(this.strokeUpdate, this.throttle);
49878 this.strokeUpdate(e);
49882 strokeBegin: function(e)
49884 var newPointGroup = {
49885 color: this.dot_color,
49889 if (typeof this.onBegin === 'function') {
49893 this.curve_data.push(newPointGroup);
49895 this.strokeUpdate(e);
49898 strokeUpdate: function(e)
49900 var rect = this.canvasEl().dom.getBoundingClientRect();
49901 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49902 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49903 var lastPoints = lastPointGroup.points;
49904 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49905 var isLastPointTooClose = lastPoint
49906 ? point.distanceTo(lastPoint) <= this.min_distance
49908 var color = lastPointGroup.color;
49909 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49910 var curve = this.addPoint(point);
49912 this.drawDot({color: color, point: point});
49915 this.drawCurve({color: color, curve: curve});
49925 strokeEnd: function(e)
49927 this.strokeUpdate(e);
49928 if (typeof this.onEnd === 'function') {
49933 addPoint: function (point) {
49934 var _lastPoints = this._lastPoints;
49935 _lastPoints.push(point);
49936 if (_lastPoints.length > 2) {
49937 if (_lastPoints.length === 3) {
49938 _lastPoints.unshift(_lastPoints[0]);
49940 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49941 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49942 _lastPoints.shift();
49948 calculateCurveWidths: function (startPoint, endPoint) {
49949 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49950 (1 - this.velocity_filter_weight) * this._lastVelocity;
49952 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49955 start: this._lastWidth
49958 this._lastVelocity = velocity;
49959 this._lastWidth = newWidth;
49963 drawDot: function (_a) {
49964 var color = _a.color, point = _a.point;
49965 var ctx = this.canvasElCtx();
49966 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49968 this.drawCurveSegment(point.x, point.y, width);
49970 ctx.fillStyle = color;
49974 drawCurve: function (_a) {
49975 var color = _a.color, curve = _a.curve;
49976 var ctx = this.canvasElCtx();
49977 var widthDelta = curve.endWidth - curve.startWidth;
49978 var drawSteps = Math.floor(curve.length()) * 2;
49980 ctx.fillStyle = color;
49981 for (var i = 0; i < drawSteps; i += 1) {
49982 var t = i / drawSteps;
49988 var x = uuu * curve.startPoint.x;
49989 x += 3 * uu * t * curve.control1.x;
49990 x += 3 * u * tt * curve.control2.x;
49991 x += ttt * curve.endPoint.x;
49992 var y = uuu * curve.startPoint.y;
49993 y += 3 * uu * t * curve.control1.y;
49994 y += 3 * u * tt * curve.control2.y;
49995 y += ttt * curve.endPoint.y;
49996 var width = curve.startWidth + ttt * widthDelta;
49997 this.drawCurveSegment(x, y, width);
50003 drawCurveSegment: function (x, y, width) {
50004 var ctx = this.canvasElCtx();
50006 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50007 this.is_empty = false;
50012 var ctx = this.canvasElCtx();
50013 var canvas = this.canvasEl().dom;
50014 ctx.fillStyle = this.bg_color;
50015 ctx.clearRect(0, 0, canvas.width, canvas.height);
50016 ctx.fillRect(0, 0, canvas.width, canvas.height);
50017 this.curve_data = [];
50019 this.is_empty = true;
50024 return this.el.select('input',true).first();
50027 canvasEl: function()
50029 return this.el.select('canvas',true).first();
50032 canvasElCtx: function()
50034 return this.el.select('canvas',true).first().dom.getContext('2d');
50037 getImage: function(type)
50039 if(this.is_empty) {
50044 return this.canvasEl().dom.toDataURL('image/'+type, 1);
50047 drawFromImage: function(img_src)
50049 var img = new Image();
50051 img.onload = function(){
50052 this.canvasElCtx().drawImage(img, 0, 0);
50057 this.is_empty = false;
50060 selectImage: function()
50062 this.fileEl().dom.click();
50065 uploadImage: function(e)
50067 var reader = new FileReader();
50069 reader.onload = function(e){
50070 var img = new Image();
50071 img.onload = function(){
50073 this.canvasElCtx().drawImage(img, 0, 0);
50075 img.src = e.target.result;
50078 reader.readAsDataURL(e.target.files[0]);
50081 // Bezier Point Constructor
50082 Point: (function () {
50083 function Point(x, y, time) {
50086 this.time = time || Date.now();
50088 Point.prototype.distanceTo = function (start) {
50089 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50091 Point.prototype.equals = function (other) {
50092 return this.x === other.x && this.y === other.y && this.time === other.time;
50094 Point.prototype.velocityFrom = function (start) {
50095 return this.time !== start.time
50096 ? this.distanceTo(start) / (this.time - start.time)
50103 // Bezier Constructor
50104 Bezier: (function () {
50105 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50106 this.startPoint = startPoint;
50107 this.control2 = control2;
50108 this.control1 = control1;
50109 this.endPoint = endPoint;
50110 this.startWidth = startWidth;
50111 this.endWidth = endWidth;
50113 Bezier.fromPoints = function (points, widths, scope) {
50114 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50115 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50116 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50118 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50119 var dx1 = s1.x - s2.x;
50120 var dy1 = s1.y - s2.y;
50121 var dx2 = s2.x - s3.x;
50122 var dy2 = s2.y - s3.y;
50123 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50124 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50125 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50126 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50127 var dxm = m1.x - m2.x;
50128 var dym = m1.y - m2.y;
50129 var k = l2 / (l1 + l2);
50130 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50131 var tx = s2.x - cm.x;
50132 var ty = s2.y - cm.y;
50134 c1: new scope.Point(m1.x + tx, m1.y + ty),
50135 c2: new scope.Point(m2.x + tx, m2.y + ty)
50138 Bezier.prototype.length = function () {
50143 for (var i = 0; i <= steps; i += 1) {
50145 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50146 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50148 var xdiff = cx - px;
50149 var ydiff = cy - py;
50150 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50157 Bezier.prototype.point = function (t, start, c1, c2, end) {
50158 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50159 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50160 + (3.0 * c2 * (1.0 - t) * t * t)
50161 + (end * t * t * t);
50166 throttleStroke: function(fn, wait) {
50167 if (wait === void 0) { wait = 250; }
50169 var timeout = null;
50173 var later = function () {
50174 previous = Date.now();
50176 result = fn.apply(storedContext, storedArgs);
50178 storedContext = null;
50182 return function wrapper() {
50184 for (var _i = 0; _i < arguments.length; _i++) {
50185 args[_i] = arguments[_i];
50187 var now = Date.now();
50188 var remaining = wait - (now - previous);
50189 storedContext = this;
50191 if (remaining <= 0 || remaining > wait) {
50193 clearTimeout(timeout);
50197 result = fn.apply(storedContext, storedArgs);
50199 storedContext = null;
50203 else if (!timeout) {
50204 timeout = window.setTimeout(later, remaining);
50214 // old names for form elements
50215 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
50216 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
50217 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
50218 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
50219 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
50220 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
50221 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
50222 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
50223 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
50224 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
50225 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
50226 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
50227 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
50228 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
50229 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
50230 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
50231 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
50232 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
50233 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
50234 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
50235 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
50236 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
50237 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
50238 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
50239 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
50240 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
50242 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
50243 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50245 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
50246 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
50248 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
50249 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50250 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
50251 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator