2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};
22 Roo.namespace('Roo.bootstrap.form.HtmlEditorToolbar');
25 * Ext JS Library 1.1.1
26 * Copyright(c) 2006-2007, Ext JS, LLC.
28 * Originally Released Under LGPL - original licence link has changed is not relivant.
31 * <script type="text/javascript">
37 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
38 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
39 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
42 * @param {Object} config The config object
44 Roo.Shadow = function(config){
45 Roo.apply(this, config);
46 if(typeof this.mode != "string"){
47 this.mode = this.defaultMode;
49 var o = this.offset, a = {h: 0};
50 var rad = Math.floor(this.offset/2);
51 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
57 a.l -= this.offset + rad;
58 a.t -= this.offset + rad;
69 a.l -= (this.offset - rad);
70 a.t -= this.offset + rad;
72 a.w -= (this.offset - rad)*2;
83 a.l -= (this.offset - rad);
84 a.t -= (this.offset - rad);
86 a.w -= (this.offset + rad + 1);
87 a.h -= (this.offset + rad);
96 Roo.Shadow.prototype = {
99 * The shadow display mode. Supports the following options:<br />
100 * sides: Shadow displays on both sides and bottom only<br />
101 * frame: Shadow displays equally on all four sides<br />
102 * drop: Traditional bottom-right drop shadow (default)
106 * @cfg {String} offset
107 * The number of pixels to offset the shadow from the element (defaults to 4)
115 * Displays the shadow under the target element
116 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
118 show : function(target){
119 target = Roo.get(target);
121 this.el = Roo.Shadow.Pool.pull();
122 if(this.el.dom.nextSibling != target.dom){
123 this.el.insertBefore(target);
126 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
128 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
131 target.getLeft(true),
136 this.el.dom.style.display = "block";
140 * Returns true if the shadow is visible, else false
142 isVisible : function(){
143 return this.el ? true : false;
147 * Direct alignment when values are already available. Show must be called at least once before
148 * calling this method to ensure it is initialized.
149 * @param {Number} left The target element left position
150 * @param {Number} top The target element top position
151 * @param {Number} width The target element width
152 * @param {Number} height The target element height
154 realign : function(l, t, w, h){
158 var a = this.adjusts, d = this.el.dom, s = d.style;
160 s.left = (l+a.l)+"px";
161 s.top = (t+a.t)+"px";
162 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
164 if(s.width != sws || s.height != shs){
168 var cn = d.childNodes;
169 var sww = Math.max(0, (sw-12))+"px";
170 cn[0].childNodes[1].style.width = sww;
171 cn[1].childNodes[1].style.width = sww;
172 cn[2].childNodes[1].style.width = sww;
173 cn[1].style.height = Math.max(0, (sh-12))+"px";
183 this.el.dom.style.display = "none";
184 Roo.Shadow.Pool.push(this.el);
190 * Adjust the z-index of this shadow
191 * @param {Number} zindex The new z-index
193 setZIndex : function(z){
196 this.el.setStyle("z-index", z);
201 // Private utility class that manages the internal Shadow cache
202 Roo.Shadow.Pool = function(){
204 var markup = Roo.isIE ?
205 '<div class="x-ie-shadow"></div>' :
206 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
211 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
212 sh.autoBoxAdjust = false;
224 * base class for bootstrap elements.
228 Roo.bootstrap = Roo.bootstrap || {};
230 * @class Roo.bootstrap.Component
231 * @extends Roo.Component
233 * @children Roo.bootstrap.Component
234 * Bootstrap Component base class
235 * @cfg {String} cls css class
236 * @cfg {String} style any extra css
237 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
238 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
239 * @cfg {string} dataId cutomer id
240 * @cfg {string} name Specifies name attribute
241 * @cfg {string} tooltip Text for the tooltip
242 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
243 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
246 * Do not use directly - it does not do anything..
247 * @param {Object} config The config object
252 Roo.bootstrap.Component = function(config){
253 Roo.bootstrap.Component.superclass.constructor.call(this, config);
257 * @event childrenrendered
258 * Fires when the children have been rendered..
259 * @param {Roo.bootstrap.Component} this
261 "childrenrendered" : true
270 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
273 allowDomMove : false, // to stop relocations in parent onRender...
283 * Initialize Events for the element
285 initEvents : function() { },
291 can_build_overlaid : true,
293 container_method : false,
300 // returns the parent component..
301 return Roo.ComponentMgr.get(this.parentId)
307 onRender : function(ct, position)
309 // Roo.log("Call onRender: " + this.xtype);
311 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
314 if (this.el.attr('xtype')) {
315 this.el.attr('xtypex', this.el.attr('xtype'));
316 this.el.dom.removeAttribute('xtype');
326 var cfg = Roo.apply({}, this.getAutoCreate());
328 cfg.id = this.id || Roo.id();
330 // fill in the extra attributes
331 if (this.xattr && typeof(this.xattr) =='object') {
332 for (var i in this.xattr) {
333 cfg[i] = this.xattr[i];
338 cfg.dataId = this.dataId;
342 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
345 if (this.style) { // fixme needs to support more complex style data.
346 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
350 cfg.name = this.name;
353 this.el = ct.createChild(cfg, position);
356 this.tooltipEl().attr('tooltip', this.tooltip);
359 if(this.tabIndex !== undefined){
360 this.el.dom.setAttribute('tabIndex', this.tabIndex);
367 * Fetch the element to add children to
368 * @return {Roo.Element} defaults to this.el
370 getChildContainer : function()
374 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
376 return Roo.get(document.body);
380 * Fetch the element to display the tooltip on.
381 * @return {Roo.Element} defaults to this.el
383 tooltipEl : function()
388 addxtype : function(tree,cntr)
393 cn = Roo.factory(tree);
394 //Roo.log(['addxtype', cn]);
396 cn.parentType = this.xtype; //??
397 cn.parentId = this.id;
401 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
402 if (typeof(cn.container_method) == 'string') {
403 cntr = cn.container_method;
407 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
409 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
411 var build_from_html = Roo.XComponent.build_from_html;
413 var is_body = (tree.xtype == 'Body') ;
415 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
417 var self_cntr_el = Roo.get(this[cntr](false));
419 // do not try and build conditional elements
420 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
424 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
425 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
426 return this.addxtypeChild(tree,cntr, is_body);
429 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
432 return this.addxtypeChild(Roo.apply({}, tree),cntr);
435 Roo.log('skipping render');
441 if (!build_from_html) {
445 // this i think handles overlaying multiple children of the same type
446 // with the sam eelement.. - which might be buggy..
448 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
454 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
458 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
465 addxtypeChild : function (tree, cntr, is_body)
467 Roo.debug && Roo.log('addxtypeChild:' + cntr);
468 Roo.log('ADDXTYPECHILD');
470 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
473 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
474 (typeof(tree['flexy:foreach']) != 'undefined');
478 skip_children = false;
479 // render the element if it's not BODY.
482 // if parent was disabled, then do not try and create the children..
483 if(!this[cntr](true)){
488 cn = Roo.factory(tree);
491 cn.parentType = this.xtype; //??
492 cn.parentId = this.id;
494 var build_from_html = Roo.XComponent.build_from_html;
497 // does the container contain child eleemnts with 'xtype' attributes.
498 // that match this xtype..
499 // note - when we render we create these as well..
500 // so we should check to see if body has xtype set.
501 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
503 var self_cntr_el = Roo.get(this[cntr](false));
504 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
506 //Roo.log(Roo.XComponent.build_from_html);
507 //Roo.log("got echild:");
510 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
511 // and are not displayed -this causes this to use up the wrong element when matching.
512 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
515 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
516 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
523 //echild.dom.removeAttribute('xtype');
525 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
526 Roo.debug && Roo.log(self_cntr_el);
527 Roo.debug && Roo.log(echild);
528 Roo.debug && Roo.log(cn);
534 // if object has flexy:if - then it may or may not be rendered.
535 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
536 // skip a flexy if element.
537 Roo.debug && Roo.log('skipping render');
538 Roo.debug && Roo.log(tree);
540 Roo.debug && Roo.log('skipping all children');
541 skip_children = true;
546 // actually if flexy:foreach is found, we really want to create
547 // multiple copies here...
549 //Roo.log(this[cntr]());
550 // some elements do not have render methods.. like the layouts...
552 if(this[cntr](true) === false){
557 cn.render && cn.render(this[cntr](true));
560 // then add the element..
567 if (typeof (tree.menu) != 'undefined') {
568 tree.menu.parentType = cn.xtype;
569 tree.menu.triggerEl = cn.el;
570 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
574 if (!tree.items || !tree.items.length) {
576 //Roo.log(["no children", this]);
581 var items = tree.items;
584 //Roo.log(items.length);
586 if (!skip_children) {
587 for(var i =0;i < items.length;i++) {
588 // Roo.log(['add child', items[i]]);
589 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
595 //Roo.log("fire childrenrendered");
597 cn.fireEvent('childrenrendered', this);
603 * Set the element that will be used to show or hide
605 setVisibilityEl : function(el)
607 this.visibilityEl = el;
611 * Get the element that will be used to show or hide
613 getVisibilityEl : function()
615 if (typeof(this.visibilityEl) == 'object') {
616 return this.visibilityEl;
619 if (typeof(this.visibilityEl) == 'string') {
620 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
627 * Show a component - removes 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().removeClass(['hidden','d-none']);
637 this.fireEvent('show', this);
642 * Hide a component - adds 'hidden' class
646 if(!this.getVisibilityEl()){
650 this.getVisibilityEl().addClass(['hidden','d-none']);
652 this.fireEvent('hide', this);
665 * @class Roo.bootstrap.Element
666 * @extends Roo.bootstrap.Component
667 * @children Roo.bootstrap.Component
668 * Bootstrap Element class (basically a DIV used to make random stuff )
670 * @cfg {String} html contents of the element
671 * @cfg {String} tag tag of the element
672 * @cfg {String} cls class of the element
673 * @cfg {Boolean} preventDefault (true|false) default false
674 * @cfg {Boolean} clickable (true|false) default false
675 * @cfg {String} role default blank - set to button to force cursor pointer
679 * Create a new Element
680 * @param {Object} config The config object
683 Roo.bootstrap.Element = function(config){
684 Roo.bootstrap.Element.superclass.constructor.call(this, config);
690 * When a element is chick
691 * @param {Roo.bootstrap.Element} this
692 * @param {Roo.EventObject} e
700 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
705 preventDefault: false,
710 getAutoCreate : function(){
714 // cls: this.cls, double assign in parent class Component.js :: onRender
717 if (this.role !== false) {
718 cfg.role = this.role;
724 initEvents: function()
726 Roo.bootstrap.Element.superclass.initEvents.call(this);
729 this.el.on('click', this.onClick, this);
735 onClick : function(e)
737 if(this.preventDefault){
741 this.fireEvent('click', this, e); // why was this double click before?
749 getValue : function()
751 return this.el.dom.innerHTML;
754 setValue : function(value)
756 this.el.dom.innerHTML = value;
771 * @class Roo.bootstrap.DropTarget
772 * @extends Roo.bootstrap.Element
773 * Bootstrap DropTarget class
775 * @cfg {string} name dropable name
778 * Create a new Dropable Area
779 * @param {Object} config The config object
782 Roo.bootstrap.DropTarget = function(config){
783 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
789 * When a element is chick
790 * @param {Roo.bootstrap.Element} this
791 * @param {Roo.EventObject} e
797 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
800 getAutoCreate : function(){
805 initEvents: function()
807 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
808 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
811 drop : this.dragDrop.createDelegate(this),
812 enter : this.dragEnter.createDelegate(this),
813 out : this.dragOut.createDelegate(this),
814 over : this.dragOver.createDelegate(this)
818 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
821 dragDrop : function(source,e,data)
823 // user has to decide how to impliment this.
826 //this.fireEvent('drop', this, source, e ,data);
830 dragEnter : function(n, dd, e, data)
832 // probably want to resize the element to match the dropped element..
834 this.originalSize = this.el.getSize();
835 this.el.setSize( n.el.getSize());
836 this.dropZone.DDM.refreshCache(this.name);
837 Roo.log([n, dd, e, data]);
840 dragOut : function(value)
842 // resize back to normal
844 this.el.setSize(this.originalSize);
845 this.dropZone.resetConstraints();
848 dragOver : function()
865 * @class Roo.bootstrap.Body
866 * @extends Roo.bootstrap.Component
867 * @children Roo.bootstrap.Component
868 * @parent none builder
869 * Bootstrap Body class
873 * @param {Object} config The config object
876 Roo.bootstrap.Body = function(config){
878 config = config || {};
880 Roo.bootstrap.Body.superclass.constructor.call(this, config);
881 this.el = Roo.get(config.el ? config.el : document.body );
882 if (this.cls && this.cls.length) {
883 Roo.get(document.body).addClass(this.cls);
887 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
889 is_body : true,// just to make sure it's constructed?
894 onRender : function(ct, position)
896 /* Roo.log("Roo.bootstrap.Body - onRender");
897 if (this.cls && this.cls.length) {
898 Roo.get(document.body).addClass(this.cls);
917 * @class Roo.bootstrap.ButtonGroup
918 * @extends Roo.bootstrap.Component
919 * Bootstrap ButtonGroup class
920 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
922 * @cfg {String} size lg | sm | xs (default empty normal)
923 * @cfg {String} align vertical | justified (default none)
924 * @cfg {String} direction up | down (default down)
925 * @cfg {Boolean} toolbar false | true
926 * @cfg {Boolean} btn true | false
931 * @param {Object} config The config object
934 Roo.bootstrap.ButtonGroup = function(config){
935 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
938 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
946 getAutoCreate : function(){
952 cfg.html = this.html || cfg.html;
963 if (['vertical','justified'].indexOf(this.align)!==-1) {
964 cfg.cls = 'btn-group-' + this.align;
966 if (this.align == 'justified') {
967 console.log(this.items);
971 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
972 cfg.cls += ' btn-group-' + this.size;
975 if (this.direction == 'up') {
976 cfg.cls += ' dropup' ;
982 * Add a button to the group (similar to NavItem API.)
984 addItem : function(cfg)
986 var cn = new Roo.bootstrap.Button(cfg);
988 cn.parentId = this.id;
989 cn.onRender(this.el, null);
1003 * @class Roo.bootstrap.Button
1004 * @extends Roo.bootstrap.Component
1005 * Bootstrap Button class
1006 * @cfg {String} html The button content
1007 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1008 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1009 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1010 * @cfg {String} size (lg|sm|xs)
1011 * @cfg {String} tag (a|input|submit)
1012 * @cfg {String} href empty or href
1013 * @cfg {Boolean} disabled default false;
1014 * @cfg {Boolean} isClose default false;
1015 * @cfg {String} glyphicon depricated - use fa
1016 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1017 * @cfg {String} badge text for badge
1018 * @cfg {String} theme (default|glow)
1019 * @cfg {Boolean} inverse dark themed version
1020 * @cfg {Boolean} toggle is it a slidy toggle button
1021 * @cfg {Boolean} pressed default null - if the button ahs active state
1022 * @cfg {String} ontext text for on slidy toggle state
1023 * @cfg {String} offtext text for off slidy toggle state
1024 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1025 * @cfg {Boolean} removeClass remove the standard class..
1026 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1027 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1028 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1031 * Create a new button
1032 * @param {Object} config The config object
1036 Roo.bootstrap.Button = function(config){
1037 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1043 * When a button is pressed
1044 * @param {Roo.bootstrap.Button} btn
1045 * @param {Roo.EventObject} e
1050 * When a button is double clicked
1051 * @param {Roo.bootstrap.Button} btn
1052 * @param {Roo.EventObject} e
1057 * After the button has been toggles
1058 * @param {Roo.bootstrap.Button} btn
1059 * @param {Roo.EventObject} e
1060 * @param {boolean} pressed (also available as button.pressed)
1066 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1087 preventDefault: true,
1096 getAutoCreate : function(){
1104 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1105 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1106 this.tag = 'button';
1110 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1112 if (this.toggle == true) {
1115 cls: 'slider-frame roo-button',
1119 'data-on-text':'ON',
1120 'data-off-text':'OFF',
1121 cls: 'slider-button',
1126 // why are we validating the weights?
1127 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1128 cfg.cls += ' ' + this.weight;
1135 cfg.cls += ' close';
1137 cfg["aria-hidden"] = true;
1139 cfg.html = "×";
1145 if (this.theme==='default') {
1146 cfg.cls = 'btn roo-button';
1148 //if (this.parentType != 'Navbar') {
1149 this.weight = this.weight.length ? this.weight : 'default';
1151 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1153 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1154 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1155 cfg.cls += ' btn-' + outline + weight;
1156 if (this.weight == 'default') {
1158 cfg.cls += ' btn-' + this.weight;
1161 } else if (this.theme==='glow') {
1164 cfg.cls = 'btn-glow roo-button';
1166 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1168 cfg.cls += ' ' + this.weight;
1174 this.cls += ' inverse';
1178 if (this.active || this.pressed === true) {
1179 cfg.cls += ' active';
1182 if (this.disabled) {
1183 cfg.disabled = 'disabled';
1187 Roo.log('changing to ul' );
1189 this.glyphicon = 'caret';
1190 if (Roo.bootstrap.version == 4) {
1191 this.fa = 'caret-down';
1196 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1198 //gsRoo.log(this.parentType);
1199 if (this.parentType === 'Navbar' && !this.parent().bar) {
1200 Roo.log('changing to li?');
1209 href : this.href || '#'
1212 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1213 cfg.cls += ' dropdown';
1220 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1222 if (this.glyphicon) {
1223 cfg.html = ' ' + cfg.html;
1228 cls: 'glyphicon glyphicon-' + this.glyphicon
1233 cfg.html = ' ' + cfg.html;
1238 cls: 'fa fas fa-' + this.fa
1248 // cfg.cls='btn roo-button';
1252 var value = cfg.html;
1257 cls: 'glyphicon glyphicon-' + this.glyphicon,
1264 cls: 'fa fas fa-' + this.fa,
1269 var bw = this.badge_weight.length ? this.badge_weight :
1270 (this.weight.length ? this.weight : 'secondary');
1271 bw = bw == 'default' ? 'secondary' : bw;
1277 cls: 'badge badge-' + bw,
1286 cfg.cls += ' dropdown';
1287 cfg.html = typeof(cfg.html) != 'undefined' ?
1288 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1291 if (cfg.tag !== 'a' && this.href !== '') {
1292 throw "Tag must be a to set href.";
1293 } else if (this.href.length > 0) {
1294 cfg.href = this.href;
1297 if(this.removeClass){
1302 cfg.target = this.target;
1307 initEvents: function() {
1308 // Roo.log('init events?');
1309 // Roo.log(this.el.dom);
1312 if (typeof (this.menu) != 'undefined') {
1313 this.menu.parentType = this.xtype;
1314 this.menu.triggerEl = this.el;
1315 this.addxtype(Roo.apply({}, this.menu));
1319 if (this.el.hasClass('roo-button')) {
1320 this.el.on('click', this.onClick, this);
1321 this.el.on('dblclick', this.onDblClick, this);
1323 this.el.select('.roo-button').on('click', this.onClick, this);
1324 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1328 if(this.removeClass){
1329 this.el.on('click', this.onClick, this);
1332 if (this.group === true) {
1333 if (this.pressed === false || this.pressed === true) {
1336 this.pressed = false;
1337 this.setActive(this.pressed);
1342 this.el.enableDisplayMode();
1345 onClick : function(e)
1347 if (this.disabled) {
1351 Roo.log('button on click ');
1352 if(this.href === '' || this.preventDefault){
1361 this.setActive(true);
1362 var pi = this.parent().items;
1363 for (var i = 0;i < pi.length;i++) {
1364 if (this == pi[i]) {
1367 if (pi[i].el.hasClass('roo-button')) {
1368 pi[i].setActive(false);
1371 this.fireEvent('click', this, e);
1375 if (this.pressed === true || this.pressed === false) {
1376 this.toggleActive(e);
1380 this.fireEvent('click', this, e);
1382 onDblClick: function(e)
1384 if (this.disabled) {
1387 if(this.preventDefault){
1390 this.fireEvent('dblclick', this, e);
1393 * Enables this button
1397 this.disabled = false;
1398 this.el.removeClass('disabled');
1399 this.el.dom.removeAttribute("disabled");
1403 * Disable this button
1405 disable : function()
1407 this.disabled = true;
1408 this.el.addClass('disabled');
1409 this.el.attr("disabled", "disabled")
1412 * sets the active state on/off,
1413 * @param {Boolean} state (optional) Force a particular state
1415 setActive : function(v) {
1417 this.el[v ? 'addClass' : 'removeClass']('active');
1421 * toggles the current active state
1423 toggleActive : function(e)
1425 this.setActive(!this.pressed); // this modifies pressed...
1426 this.fireEvent('toggle', this, e, this.pressed);
1429 * get the current active state
1430 * @return {boolean} true if it's active
1432 isActive : function()
1434 return this.el.hasClass('active');
1437 * set the text of the first selected button
1439 setText : function(str)
1441 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1444 * get the text of the first selected button
1446 getText : function()
1448 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1451 setWeight : function(str)
1453 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1454 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1456 var outline = this.outline ? 'outline-' : '';
1457 if (str == 'default') {
1458 this.el.addClass('btn-default btn-outline-secondary');
1461 this.el.addClass('btn-' + outline + str);
1466 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1468 Roo.bootstrap.Button.weights = [
1488 * @class Roo.bootstrap.Column
1489 * @extends Roo.bootstrap.Component
1490 * @children Roo.bootstrap.Component
1491 * Bootstrap Column class
1492 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1493 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1494 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1495 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1496 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1497 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1498 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1499 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1502 * @cfg {Boolean} hidden (true|false) hide the element
1503 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1504 * @cfg {String} fa (ban|check|...) font awesome icon
1505 * @cfg {Number} fasize (1|2|....) font awsome size
1507 * @cfg {String} icon (info-sign|check|...) glyphicon name
1509 * @cfg {String} html content of column.
1512 * Create a new Column
1513 * @param {Object} config The config object
1516 Roo.bootstrap.Column = function(config){
1517 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1520 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1538 getAutoCreate : function(){
1539 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1547 var sizes = ['xs','sm','md','lg'];
1548 sizes.map(function(size ,ix){
1549 //Roo.log( size + ':' + settings[size]);
1551 if (settings[size+'off'] !== false) {
1552 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1555 if (settings[size] === false) {
1559 if (!settings[size]) { // 0 = hidden
1560 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1562 for (var i = ix; i > -1; i--) {
1563 cfg.cls += ' d-' + sizes[i] + '-none';
1569 cfg.cls += ' col-' + size + '-' + settings[size] + (
1570 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1576 cfg.cls += ' hidden';
1579 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1580 cfg.cls +=' alert alert-' + this.alert;
1584 if (this.html.length) {
1585 cfg.html = this.html;
1589 if (this.fasize > 1) {
1590 fasize = ' fa-' + this.fasize + 'x';
1592 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1597 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1616 * @class Roo.bootstrap.Container
1617 * @extends Roo.bootstrap.Component
1618 * @children Roo.bootstrap.Component
1620 * Bootstrap Container class
1621 * @cfg {Boolean} jumbotron is it a jumbotron element
1622 * @cfg {String} html content of element
1623 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1624 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1625 * @cfg {String} header content of header (for panel)
1626 * @cfg {String} footer content of footer (for panel)
1627 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1628 * @cfg {String} tag (header|aside|section) type of HTML tag.
1629 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1630 * @cfg {String} fa font awesome icon
1631 * @cfg {String} icon (info-sign|check|...) glyphicon name
1632 * @cfg {Boolean} hidden (true|false) hide the element
1633 * @cfg {Boolean} expandable (true|false) default false
1634 * @cfg {Boolean} expanded (true|false) default true
1635 * @cfg {String} rheader contet on the right of header
1636 * @cfg {Boolean} clickable (true|false) default false
1640 * Create a new Container
1641 * @param {Object} config The config object
1644 Roo.bootstrap.Container = function(config){
1645 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1651 * After the panel has been expand
1653 * @param {Roo.bootstrap.Container} this
1658 * After the panel has been collapsed
1660 * @param {Roo.bootstrap.Container} this
1665 * When a element is chick
1666 * @param {Roo.bootstrap.Container} this
1667 * @param {Roo.EventObject} e
1673 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1691 getChildContainer : function() {
1697 if (this.panel.length) {
1698 return this.el.select('.panel-body',true).first();
1705 getAutoCreate : function(){
1708 tag : this.tag || 'div',
1712 if (this.jumbotron) {
1713 cfg.cls = 'jumbotron';
1718 // - this is applied by the parent..
1720 // cfg.cls = this.cls + '';
1723 if (this.sticky.length) {
1725 var bd = Roo.get(document.body);
1726 if (!bd.hasClass('bootstrap-sticky')) {
1727 bd.addClass('bootstrap-sticky');
1728 Roo.select('html',true).setStyle('height', '100%');
1731 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1735 if (this.well.length) {
1736 switch (this.well) {
1739 cfg.cls +=' well well-' +this.well;
1748 cfg.cls += ' hidden';
1752 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1753 cfg.cls +=' alert alert-' + this.alert;
1758 if (this.panel.length) {
1759 cfg.cls += ' panel panel-' + this.panel;
1761 if (this.header.length) {
1765 if(this.expandable){
1767 cfg.cls = cfg.cls + ' expandable';
1771 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1779 cls : 'panel-title',
1780 html : (this.expandable ? ' ' : '') + this.header
1784 cls: 'panel-header-right',
1790 cls : 'panel-heading',
1791 style : this.expandable ? 'cursor: pointer' : '',
1799 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1804 if (this.footer.length) {
1806 cls : 'panel-footer',
1815 body.html = this.html || cfg.html;
1816 // prefix with the icons..
1818 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1821 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1826 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1827 cfg.cls = 'container';
1833 initEvents: function()
1835 if(this.expandable){
1836 var headerEl = this.headerEl();
1839 headerEl.on('click', this.onToggleClick, this);
1844 this.el.on('click', this.onClick, this);
1849 onToggleClick : function()
1851 var headerEl = this.headerEl();
1867 if(this.fireEvent('expand', this)) {
1869 this.expanded = true;
1871 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1873 this.el.select('.panel-body',true).first().removeClass('hide');
1875 var toggleEl = this.toggleEl();
1881 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1886 collapse : function()
1888 if(this.fireEvent('collapse', this)) {
1890 this.expanded = false;
1892 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1893 this.el.select('.panel-body',true).first().addClass('hide');
1895 var toggleEl = this.toggleEl();
1901 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1905 toggleEl : function()
1907 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1911 return this.el.select('.panel-heading .fa',true).first();
1914 headerEl : function()
1916 if(!this.el || !this.panel.length || !this.header.length){
1920 return this.el.select('.panel-heading',true).first()
1925 if(!this.el || !this.panel.length){
1929 return this.el.select('.panel-body',true).first()
1932 titleEl : function()
1934 if(!this.el || !this.panel.length || !this.header.length){
1938 return this.el.select('.panel-title',true).first();
1941 setTitle : function(v)
1943 var titleEl = this.titleEl();
1949 titleEl.dom.innerHTML = v;
1952 getTitle : function()
1955 var titleEl = this.titleEl();
1961 return titleEl.dom.innerHTML;
1964 setRightTitle : function(v)
1966 var t = this.el.select('.panel-header-right',true).first();
1972 t.dom.innerHTML = v;
1975 onClick : function(e)
1979 this.fireEvent('click', this, e);
1984 * @class Roo.bootstrap.Card
1985 * @extends Roo.bootstrap.Component
1986 * @children Roo.bootstrap.Component
1988 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1991 * possible... may not be implemented..
1992 * @cfg {String} header_image src url of image.
1993 * @cfg {String|Object} header
1994 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1995 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1997 * @cfg {String} title
1998 * @cfg {String} subtitle
1999 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
2000 * @cfg {String} footer
2002 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
2004 * @cfg {String} margin (0|1|2|3|4|5|auto)
2005 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
2006 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
2007 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2008 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2009 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2010 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2012 * @cfg {String} padding (0|1|2|3|4|5)
2013 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2014 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2015 * @cfg {String} padding_left (0|1|2|3|4|5)
2016 * @cfg {String} padding_right (0|1|2|3|4|5)
2017 * @cfg {String} padding_x (0|1|2|3|4|5)
2018 * @cfg {String} padding_y (0|1|2|3|4|5)
2020 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2021 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2022 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2023 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2024 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2026 * @config {Boolean} dragable if this card can be dragged.
2027 * @config {String} drag_group group for drag
2028 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2029 * @config {String} drop_group group for drag
2031 * @config {Boolean} collapsable can the body be collapsed.
2032 * @config {Boolean} collapsed is the body collapsed when rendered...
2033 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2034 * @config {Boolean} rotated is the body rotated when rendered...
2037 * Create a new Container
2038 * @param {Object} config The config object
2041 Roo.bootstrap.Card = function(config){
2042 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2048 * When a element a card is dropped
2049 * @param {Roo.bootstrap.Card} this
2052 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2053 * @param {String} position 'above' or 'below'
2054 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2060 * When a element a card is rotate
2061 * @param {Roo.bootstrap.Card} this
2062 * @param {Roo.Element} n the node being dropped?
2063 * @param {Boolean} rotate status
2068 * When a card element is dragged over ready to drop (return false to block dropable)
2069 * @param {Roo.bootstrap.Card} this
2070 * @param {Object} data from dragdrop
2078 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2083 margin: '', /// may be better in component?
2113 collapsable : false,
2122 childContainer : false,
2123 dropEl : false, /// the dom placeholde element that indicates drop location.
2124 containerEl: false, // body container
2125 bodyEl: false, // card-body
2126 headerContainerEl : false, //
2128 header_imageEl : false,
2131 layoutCls : function()
2135 Roo.log(this.margin_bottom.length);
2136 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2137 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2139 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2140 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2142 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2143 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2147 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2148 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2149 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2153 // more generic support?
2161 // Roo.log("Call onRender: " + this.xtype);
2162 /* We are looking at something like this.
2164 <img src="..." class="card-img-top" alt="...">
2165 <div class="card-body">
2166 <h5 class="card-title">Card title</h5>
2167 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2169 >> this bit is really the body...
2170 <div> << we will ad dthis in hopefully it will not break shit.
2172 ** card text does not actually have any styling...
2174 <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>
2177 <a href="#" class="card-link">Card link</a>
2180 <div class="card-footer">
2181 <small class="text-muted">Last updated 3 mins ago</small>
2185 getAutoCreate : function(){
2193 if (this.weight.length && this.weight != 'light') {
2194 cfg.cls += ' text-white';
2196 cfg.cls += ' text-dark'; // need as it's nested..
2198 if (this.weight.length) {
2199 cfg.cls += ' bg-' + this.weight;
2202 cfg.cls += ' ' + this.layoutCls();
2205 var hdr_ctr = false;
2206 if (this.header.length) {
2208 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2209 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2217 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2223 if (this.collapsable) {
2226 cls : 'd-block user-select-none',
2230 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2235 hdr.cn.push(hdr_ctr);
2240 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2245 if (this.header_image.length) {
2248 cls : 'card-img-top',
2249 src: this.header_image // escape?
2254 cls : 'card-img-top d-none'
2260 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2264 if (this.collapsable || this.rotateable) {
2267 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2274 if (this.title.length) {
2278 src: this.title // escape?
2282 if (this.subtitle.length) {
2286 src: this.subtitle // escape?
2292 cls : 'roo-card-body-ctr'
2295 if (this.html.length) {
2301 // fixme ? handle objects?
2303 if (this.footer.length) {
2306 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2311 cfg.cn.push({cls : 'card-footer d-none'});
2320 getCardHeader : function()
2322 var ret = this.el.select('.card-header',true).first();
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2329 getCardFooter : function()
2331 var ret = this.el.select('.card-footer',true).first();
2332 if (ret.hasClass('d-none')) {
2333 ret.removeClass('d-none');
2338 getCardImageTop : function()
2340 var ret = this.header_imageEl;
2341 if (ret.hasClass('d-none')) {
2342 ret.removeClass('d-none');
2348 getChildContainer : function()
2354 return this.el.select('.roo-card-body-ctr',true).first();
2357 initEvents: function()
2359 this.bodyEl = this.el.select('.card-body',true).first();
2360 this.containerEl = this.getChildContainer();
2362 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2363 containerScroll: true,
2364 ddGroup: this.drag_group || 'default_card_drag_group'
2366 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2368 if (this.dropable) {
2369 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2370 containerScroll: true,
2371 ddGroup: this.drop_group || 'default_card_drag_group'
2373 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2374 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2375 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2376 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2377 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2380 if (this.collapsable) {
2381 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2383 if (this.rotateable) {
2384 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2386 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2388 this.footerEl = this.el.select('.card-footer',true).first();
2389 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2390 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2391 this.headerEl = this.el.select('.card-header',true).first();
2394 this.el.addClass('roo-card-rotated');
2395 this.fireEvent('rotate', this, true);
2397 this.header_imageEl = this.el.select('.card-img-top',true).first();
2398 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2401 getDragData : function(e)
2403 var target = this.getEl();
2405 //this.handleSelection(e);
2410 nodes: this.getEl(),
2415 dragData.ddel = target.dom ; // the div element
2416 Roo.log(target.getWidth( ));
2417 dragData.ddel.style.width = target.getWidth() + 'px';
2424 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2425 * whole Element becomes the target, and this causes the drop gesture to append.
2427 * Returns an object:
2430 position : 'below' or 'above'
2431 card : relateive to card OBJECT (or true for no cards listed)
2432 items_n : relative to nth item in list
2433 card_n : relative to nth card in list
2438 getTargetFromEvent : function(e, dragged_card_el)
2440 var target = e.getTarget();
2441 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2442 target = target.parentNode;
2453 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2454 // see if target is one of the 'cards'...
2457 //Roo.log(this.items.length);
2460 var last_card_n = 0;
2462 for (var i = 0;i< this.items.length;i++) {
2464 if (!this.items[i].el.hasClass('card')) {
2467 pos = this.getDropPoint(e, this.items[i].el.dom);
2469 cards_len = ret.cards.length;
2470 //Roo.log(this.items[i].el.dom.id);
2471 ret.cards.push(this.items[i]);
2473 if (ret.card_n < 0 && pos == 'above') {
2474 ret.position = cards_len > 0 ? 'below' : pos;
2475 ret.items_n = i > 0 ? i - 1 : 0;
2476 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2477 ret.card = ret.cards[ret.card_n];
2480 if (!ret.cards.length) {
2482 ret.position = 'below';
2486 // could not find a card.. stick it at the end..
2487 if (ret.card_n < 0) {
2488 ret.card_n = last_card_n;
2489 ret.card = ret.cards[last_card_n];
2490 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2491 ret.position = 'below';
2494 if (this.items[ret.items_n].el == dragged_card_el) {
2498 if (ret.position == 'below') {
2499 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2501 if (card_after && card_after.el == dragged_card_el) {
2508 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2510 if (card_before && card_before.el == dragged_card_el) {
2517 onNodeEnter : function(n, dd, e, data){
2520 onNodeOver : function(n, dd, e, data)
2523 var target_info = this.getTargetFromEvent(e,data.source.el);
2524 if (target_info === false) {
2525 this.dropPlaceHolder('hide');
2528 Roo.log(['getTargetFromEvent', target_info ]);
2531 if (this.fireEvent('cardover', this, [ data ]) === false) {
2535 this.dropPlaceHolder('show', target_info,data);
2539 onNodeOut : function(n, dd, e, data){
2540 this.dropPlaceHolder('hide');
2543 onNodeDrop : function(n, dd, e, data)
2546 // call drop - return false if
2548 // this could actually fail - if the Network drops..
2549 // we will ignore this at present..- client should probably reload
2550 // the whole set of cards if stuff like that fails.
2553 var info = this.getTargetFromEvent(e,data.source.el);
2554 if (info === false) {
2557 this.dropPlaceHolder('hide');
2561 this.acceptCard(data.source, info.position, info.card, info.items_n);
2565 firstChildCard : function()
2567 for (var i = 0;i< this.items.length;i++) {
2569 if (!this.items[i].el.hasClass('card')) {
2572 return this.items[i];
2574 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2579 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2581 acceptCard : function(move_card, position, next_to_card )
2583 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2587 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2589 move_card.parent().removeCard(move_card);
2592 var dom = move_card.el.dom;
2593 dom.style.width = ''; // clear with - which is set by drag.
2595 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2596 var cardel = next_to_card.el.dom;
2598 if (position == 'above' ) {
2599 cardel.parentNode.insertBefore(dom, cardel);
2600 } else if (cardel.nextSibling) {
2601 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2603 cardel.parentNode.append(dom);
2606 // card container???
2607 this.containerEl.dom.append(dom);
2610 //FIXME HANDLE card = true
2612 // add this to the correct place in items.
2614 // remove Card from items.
2617 if (this.items.length) {
2619 //Roo.log([info.items_n, info.position, this.items.length]);
2620 for (var i =0; i < this.items.length; i++) {
2621 if (i == to_items_n && position == 'above') {
2622 nitems.push(move_card);
2624 nitems.push(this.items[i]);
2625 if (i == to_items_n && position == 'below') {
2626 nitems.push(move_card);
2629 this.items = nitems;
2630 Roo.log(this.items);
2632 this.items.push(move_card);
2635 move_card.parentId = this.id;
2641 removeCard : function(c)
2643 this.items = this.items.filter(function(e) { return e != c });
2646 dom.parentNode.removeChild(dom);
2647 dom.style.width = ''; // clear with - which is set by drag.
2652 /** Decide whether to drop above or below a View node. */
2653 getDropPoint : function(e, n, dd)
2658 if (n == this.containerEl.dom) {
2661 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2662 var c = t + (b - t) / 2;
2663 var y = Roo.lib.Event.getPageY(e);
2670 onToggleCollapse : function(e)
2672 if (this.collapsed) {
2673 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2674 this.collapsableEl.addClass('show');
2675 this.collapsed = false;
2678 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2679 this.collapsableEl.removeClass('show');
2680 this.collapsed = true;
2685 onToggleRotate : function(e)
2687 this.collapsableEl.removeClass('show');
2688 this.footerEl.removeClass('d-none');
2689 this.el.removeClass('roo-card-rotated');
2690 this.el.removeClass('d-none');
2693 this.collapsableEl.addClass('show');
2694 this.rotated = false;
2695 this.fireEvent('rotate', this, this.rotated);
2698 this.el.addClass('roo-card-rotated');
2699 this.footerEl.addClass('d-none');
2700 this.el.select('.roo-collapsable').removeClass('show');
2702 this.rotated = true;
2703 this.fireEvent('rotate', this, this.rotated);
2707 dropPlaceHolder: function (action, info, data)
2709 if (this.dropEl === false) {
2710 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2714 this.dropEl.removeClass(['d-none', 'd-block']);
2715 if (action == 'hide') {
2717 this.dropEl.addClass('d-none');
2720 // FIXME - info.card == true!!!
2721 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2723 if (info.card !== true) {
2724 var cardel = info.card.el.dom;
2726 if (info.position == 'above') {
2727 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2728 } else if (cardel.nextSibling) {
2729 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2731 cardel.parentNode.append(this.dropEl.dom);
2734 // card container???
2735 this.containerEl.dom.append(this.dropEl.dom);
2738 this.dropEl.addClass('d-block roo-card-dropzone');
2740 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2747 setHeaderText: function(html)
2750 if (this.headerContainerEl) {
2751 this.headerContainerEl.dom.innerHTML = html;
2754 onHeaderImageLoad : function(ev, he)
2756 if (!this.header_image_fit_square) {
2760 var hw = he.naturalHeight / he.naturalWidth;
2763 //var w = he.dom.naturalWidth;
2766 he.style.position = 'relative';
2768 var nw = (ww * (1/hw));
2769 Roo.get(he).setSize( ww * (1/hw), ww);
2770 he.style.left = ((ww - nw)/ 2) + 'px';
2771 he.style.position = 'relative';
2782 * Card header - holder for the card header elements.
2787 * @class Roo.bootstrap.CardHeader
2788 * @extends Roo.bootstrap.Element
2789 * @parent Roo.bootstrap.Card
2790 * @children Roo.bootstrap.Component
2791 * Bootstrap CardHeader class
2793 * Create a new Card Header - that you can embed children into
2794 * @param {Object} config The config object
2797 Roo.bootstrap.CardHeader = function(config){
2798 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2801 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2804 container_method : 'getCardHeader'
2817 * Card footer - holder for the card footer elements.
2822 * @class Roo.bootstrap.CardFooter
2823 * @extends Roo.bootstrap.Element
2824 * @parent Roo.bootstrap.Card
2825 * @children Roo.bootstrap.Component
2826 * Bootstrap CardFooter class
2829 * Create a new Card Footer - that you can embed children into
2830 * @param {Object} config The config object
2833 Roo.bootstrap.CardFooter = function(config){
2834 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2837 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2840 container_method : 'getCardFooter'
2853 * Card header - holder for the card header elements.
2858 * @class Roo.bootstrap.CardImageTop
2859 * @extends Roo.bootstrap.Element
2860 * @parent Roo.bootstrap.Card
2861 * @children Roo.bootstrap.Component
2862 * Bootstrap CardImageTop class
2865 * Create a new Card Image Top container
2866 * @param {Object} config The config object
2869 Roo.bootstrap.CardImageTop = function(config){
2870 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2873 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2876 container_method : 'getCardImageTop'
2891 * @class Roo.bootstrap.ButtonUploader
2892 * @extends Roo.bootstrap.Button
2893 * Bootstrap Button Uploader class - it's a button which when you add files to it
2896 * @cfg {Number} errorTimeout default 3000
2897 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2898 * @cfg {Array} html The button text.
2899 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2902 * Create a new CardUploader
2903 * @param {Object} config The config object
2906 Roo.bootstrap.ButtonUploader = function(config){
2910 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2916 * @event beforeselect
2917 * When button is pressed, before show upload files dialog is shown
2918 * @param {Roo.bootstrap.UploaderButton} this
2921 'beforeselect' : true,
2923 * @event fired when files have been selected,
2924 * When a the download link is clicked
2925 * @param {Roo.bootstrap.UploaderButton} this
2926 * @param {Array} Array of files that have been uploaded
2933 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2936 errorTimeout : 3000,
2940 fileCollection : false,
2945 getAutoCreate : function()
2952 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2960 initEvents : function()
2963 Roo.bootstrap.Button.prototype.initEvents.call(this);
2969 this.urlAPI = (window.createObjectURL && window) ||
2970 (window.URL && URL.revokeObjectURL && URL) ||
2971 (window.webkitURL && webkitURL);
2976 cls : 'd-none roo-card-upload-selector'
2979 if (this.multiple) {
2980 im.multiple = 'multiple';
2982 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2984 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2986 this.selectorEl.on('change', this.onFileSelected, this);
2993 onClick : function(e)
2997 if ( this.fireEvent('beforeselect', this) === false) {
3001 this.selectorEl.dom.click();
3005 onFileSelected : function(e)
3009 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3012 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3013 this.selectorEl.dom.value = '';// hopefully reset..
3015 this.fireEvent('uploaded', this, files );
3023 * addCard - add an Attachment to the uploader
3024 * @param data - the data about the image to upload
3028 title : "Title of file",
3029 is_uploaded : false,
3030 src : "http://.....",
3031 srcfile : { the File upload object },
3032 mimetype : file.type,
3035 .. any other data...
3060 * @class Roo.bootstrap.Img
3061 * @extends Roo.bootstrap.Component
3062 * Bootstrap Img class
3063 * @cfg {Boolean} imgResponsive false | true
3064 * @cfg {String} border rounded | circle | thumbnail
3065 * @cfg {String} src image source
3066 * @cfg {String} alt image alternative text
3067 * @cfg {String} href a tag href
3068 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3069 * @cfg {String} xsUrl xs image source
3070 * @cfg {String} smUrl sm image source
3071 * @cfg {String} mdUrl md image source
3072 * @cfg {String} lgUrl lg image source
3073 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3076 * Create a new Input
3077 * @param {Object} config The config object
3080 Roo.bootstrap.Img = function(config){
3081 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3087 * The img click event for the img.
3088 * @param {Roo.EventObject} e
3093 * The when any image loads
3094 * @param {Roo.EventObject} e
3100 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3102 imgResponsive: true,
3111 backgroundContain : false,
3113 getAutoCreate : function()
3115 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3116 return this.createSingleImg();
3121 cls: 'roo-image-responsive-group',
3126 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3128 if(!_this[size + 'Url']){
3134 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3135 html: _this.html || cfg.html,
3136 src: _this[size + 'Url']
3139 img.cls += ' roo-image-responsive-' + size;
3141 var s = ['xs', 'sm', 'md', 'lg'];
3143 s.splice(s.indexOf(size), 1);
3145 Roo.each(s, function(ss){
3146 img.cls += ' hidden-' + ss;
3149 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3150 cfg.cls += ' img-' + _this.border;
3154 cfg.alt = _this.alt;
3167 a.target = _this.target;
3171 cfg.cn.push((_this.href) ? a : img);
3178 createSingleImg : function()
3182 cls: (this.imgResponsive) ? 'img-responsive' : '',
3184 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3187 if (this.backgroundContain) {
3188 cfg.cls += ' background-contain';
3191 cfg.html = this.html || cfg.html;
3193 if (this.backgroundContain) {
3194 cfg.style="background-image: url(" + this.src + ')';
3196 cfg.src = this.src || cfg.src;
3199 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3200 cfg.cls += ' img-' + this.border;
3217 a.target = this.target;
3222 return (this.href) ? a : cfg;
3225 initEvents: function()
3228 this.el.on('click', this.onClick, this);
3230 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3231 this.el.on('load', this.onImageLoad, this);
3233 // not sure if this works.. not tested
3234 this.el.select('img', true).on('load', this.onImageLoad, this);
3239 onClick : function(e)
3241 Roo.log('img onclick');
3242 this.fireEvent('click', this, e);
3244 onImageLoad: function(e)
3246 Roo.log('img load');
3247 this.fireEvent('load', this, e);
3251 * Sets the url of the image - used to update it
3252 * @param {String} url the url of the image
3255 setSrc : function(url)
3259 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3260 if (this.backgroundContain) {
3261 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3263 this.el.dom.src = url;
3268 this.el.select('img', true).first().dom.src = url;
3284 * @class Roo.bootstrap.Link
3285 * @extends Roo.bootstrap.Component
3286 * @children Roo.bootstrap.Component
3287 * Bootstrap Link Class (eg. '<a href>')
3289 * @cfg {String} alt image alternative text
3290 * @cfg {String} href a tag href
3291 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3292 * @cfg {String} html the content of the link.
3293 * @cfg {String} anchor name for the anchor link
3294 * @cfg {String} fa - favicon
3296 * @cfg {Boolean} preventDefault (true | false) default false
3300 * Create a new Input
3301 * @param {Object} config The config object
3304 Roo.bootstrap.Link = function(config){
3305 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3311 * The img click event for the img.
3312 * @param {Roo.EventObject} e
3318 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3322 preventDefault: false,
3328 getAutoCreate : function()
3330 var html = this.html || '';
3332 if (this.fa !== false) {
3333 html = '<i class="fa fa-' + this.fa + '"></i>';
3338 // anchor's do not require html/href...
3339 if (this.anchor === false) {
3341 cfg.href = this.href || '#';
3343 cfg.name = this.anchor;
3344 if (this.html !== false || this.fa !== false) {
3347 if (this.href !== false) {
3348 cfg.href = this.href;
3352 if(this.alt !== false){
3357 if(this.target !== false) {
3358 cfg.target = this.target;
3364 initEvents: function() {
3366 if(!this.href || this.preventDefault){
3367 this.el.on('click', this.onClick, this);
3371 onClick : function(e)
3373 if(this.preventDefault){
3376 //Roo.log('img onclick');
3377 this.fireEvent('click', this, e);
3390 * @class Roo.bootstrap.Header
3391 * @extends Roo.bootstrap.Component
3392 * @children Roo.bootstrap.Component
3393 * Bootstrap Header class
3396 * @cfg {String} html content of header
3397 * @cfg {Number} level (1|2|3|4|5|6) default 1
3400 * Create a new Header
3401 * @param {Object} config The config object
3405 Roo.bootstrap.Header = function(config){
3406 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3409 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3417 getAutoCreate : function(){
3422 tag: 'h' + (1 *this.level),
3423 html: this.html || ''
3434 * @class Roo.bootstrap.MenuMgr
3436 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3439 Roo.bootstrap.menu.Manager = function(){
3440 var menus, active, groups = {}, attached = false, lastShow = new Date();
3442 // private - called when first menu is created
3445 active = new Roo.util.MixedCollection();
3446 Roo.get(document).addKeyListener(27, function(){
3447 if(active.length > 0){
3455 if(active && active.length > 0){
3456 var c = active.clone();
3466 if(active.length < 1){
3467 Roo.get(document).un("mouseup", onMouseDown);
3475 var last = active.last();
3476 lastShow = new Date();
3479 Roo.get(document).on("mouseup", onMouseDown);
3484 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3485 m.parentMenu.activeChild = m;
3486 }else if(last && last.isVisible()){
3487 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3492 function onBeforeHide(m){
3494 m.activeChild.hide();
3496 if(m.autoHideTimer){
3497 clearTimeout(m.autoHideTimer);
3498 delete m.autoHideTimer;
3503 function onBeforeShow(m){
3504 var pm = m.parentMenu;
3505 if(!pm && !m.allowOtherMenus){
3507 }else if(pm && pm.activeChild && active != m){
3508 pm.activeChild.hide();
3512 // private this should really trigger on mouseup..
3513 function onMouseDown(e){
3514 Roo.log("on Mouse Up");
3516 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3517 Roo.log("MenuManager hideAll");
3526 function onBeforeCheck(mi, state){
3528 var g = groups[mi.group];
3529 for(var i = 0, l = g.length; i < l; i++){
3531 g[i].setChecked(false);
3540 * Hides all menus that are currently visible
3542 hideAll : function(){
3547 register : function(menu){
3551 menus[menu.id] = menu;
3552 menu.on("beforehide", onBeforeHide);
3553 menu.on("hide", onHide);
3554 menu.on("beforeshow", onBeforeShow);
3555 menu.on("show", onShow);
3557 if(g && menu.events["checkchange"]){
3561 groups[g].push(menu);
3562 menu.on("checkchange", onCheck);
3567 * Returns a {@link Roo.menu.Menu} object
3568 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3569 * be used to generate and return a new Menu instance.
3571 get : function(menu){
3572 if(typeof menu == "string"){ // menu id
3574 }else if(menu.events){ // menu instance
3577 /*else if(typeof menu.length == 'number'){ // array of menu items?
3578 return new Roo.bootstrap.Menu({items:menu});
3579 }else{ // otherwise, must be a config
3580 return new Roo.bootstrap.Menu(menu);
3587 unregister : function(menu){
3588 delete menus[menu.id];
3589 menu.un("beforehide", onBeforeHide);
3590 menu.un("hide", onHide);
3591 menu.un("beforeshow", onBeforeShow);
3592 menu.un("show", onShow);
3594 if(g && menu.events["checkchange"]){
3595 groups[g].remove(menu);
3596 menu.un("checkchange", onCheck);
3601 registerCheckable : function(menuItem){
3602 var g = menuItem.group;
3607 groups[g].push(menuItem);
3608 menuItem.on("beforecheckchange", onBeforeCheck);
3613 unregisterCheckable : function(menuItem){
3614 var g = menuItem.group;
3616 groups[g].remove(menuItem);
3617 menuItem.un("beforecheckchange", onBeforeCheck);
3623 * @class Roo.bootstrap.menu.Menu
3624 * @extends Roo.bootstrap.Component
3626 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3628 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3630 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3631 * @cfg {bool} hidden if the menu should be hidden when rendered.
3632 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3633 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3634 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3635 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3639 * @param {Object} config The config objectQ
3643 Roo.bootstrap.menu.Menu = function(config){
3645 if (config.type == 'treeview') {
3646 // normally menu's are drawn attached to the document to handle layering etc..
3647 // however treeview (used by the docs menu is drawn into the parent element)
3648 this.container_method = 'getChildContainer';
3651 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3652 if (this.registerMenu && this.type != 'treeview') {
3653 Roo.bootstrap.menu.Manager.register(this);
3660 * Fires before this menu is displayed (return false to block)
3661 * @param {Roo.menu.Menu} this
3666 * Fires before this menu is hidden (return false to block)
3667 * @param {Roo.menu.Menu} this
3672 * Fires after this menu is displayed
3673 * @param {Roo.menu.Menu} this
3678 * Fires after this menu is hidden
3679 * @param {Roo.menu.Menu} this
3684 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3685 * @param {Roo.menu.Menu} this
3686 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3687 * @param {Roo.EventObject} e
3692 * Fires when the mouse is hovering over this menu
3693 * @param {Roo.menu.Menu} this
3694 * @param {Roo.EventObject} e
3695 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3700 * Fires when the mouse exits this menu
3701 * @param {Roo.menu.Menu} this
3702 * @param {Roo.EventObject} e
3703 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3708 * Fires when a menu item contained in this menu is clicked
3709 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3710 * @param {Roo.EventObject} e
3714 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3717 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3721 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3724 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3726 registerMenu : true,
3728 menuItems :false, // stores the menu items..
3738 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3740 hideTrigger : false,
3745 getChildContainer : function() {
3749 getAutoCreate : function(){
3751 //if (['right'].indexOf(this.align)!==-1) {
3752 // cfg.cn[1].cls += ' pull-right'
3757 cls : 'dropdown-menu shadow' ,
3758 style : 'z-index:1000'
3762 if (this.type === 'submenu') {
3763 cfg.cls = 'submenu active';
3765 if (this.type === 'treeview') {
3766 cfg.cls = 'treeview-menu';
3771 initEvents : function() {
3773 // Roo.log("ADD event");
3774 // Roo.log(this.triggerEl.dom);
3775 if (this.triggerEl) {
3777 this.triggerEl.on('click', this.onTriggerClick, this);
3779 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3781 if (!this.hideTrigger) {
3782 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3783 // dropdown toggle on the 'a' in BS4?
3784 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3786 this.triggerEl.addClass('dropdown-toggle');
3792 this.el.on('touchstart' , this.onTouch, this);
3794 this.el.on('click' , this.onClick, this);
3796 this.el.on("mouseover", this.onMouseOver, this);
3797 this.el.on("mouseout", this.onMouseOut, this);
3801 findTargetItem : function(e)
3803 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3807 //Roo.log(t); Roo.log(t.id);
3809 //Roo.log(this.menuitems);
3810 return this.menuitems.get(t.id);
3812 //return this.items.get(t.menuItemId);
3818 onTouch : function(e)
3820 Roo.log("menu.onTouch");
3821 //e.stopEvent(); this make the user popdown broken
3825 onClick : function(e)
3827 Roo.log("menu.onClick");
3829 var t = this.findTargetItem(e);
3830 if(!t || t.isContainer){
3835 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3836 if(t == this.activeItem && t.shouldDeactivate(e)){
3837 this.activeItem.deactivate();
3838 delete this.activeItem;
3842 this.setActiveItem(t, true);
3850 Roo.log('pass click event');
3854 this.fireEvent("click", this, t, e);
3858 if(!t.href.length || t.href == '#'){
3859 (function() { _this.hide(); }).defer(100);
3864 onMouseOver : function(e){
3865 var t = this.findTargetItem(e);
3868 // if(t.canActivate && !t.disabled){
3869 // this.setActiveItem(t, true);
3873 this.fireEvent("mouseover", this, e, t);
3875 isVisible : function(){
3876 return !this.hidden;
3878 onMouseOut : function(e){
3879 var t = this.findTargetItem(e);
3882 // if(t == this.activeItem && t.shouldDeactivate(e)){
3883 // this.activeItem.deactivate();
3884 // delete this.activeItem;
3887 this.fireEvent("mouseout", this, e, t);
3892 * Displays this menu relative to another element
3893 * @param {String/HTMLElement/Roo.Element} element The element to align to
3894 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3895 * the element (defaults to this.defaultAlign)
3896 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3898 show : function(el, pos, parentMenu)
3900 if (false === this.fireEvent("beforeshow", this)) {
3901 Roo.log("show canceled");
3904 this.parentMenu = parentMenu;
3908 this.el.addClass('show'); // show otherwise we do not know how big we are..
3910 var xy = this.el.getAlignToXY(el, pos);
3912 // bl-tl << left align below
3913 // tl-bl << left align
3915 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3916 // if it goes to far to the right.. -> align left.
3917 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3920 // was left align - go right?
3921 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3924 // goes down the bottom
3925 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3927 var a = this.align.replace('?', '').split('-');
3928 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3932 this.showAt( xy , parentMenu, false);
3935 * Displays this menu at a specific xy position
3936 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3937 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3939 showAt : function(xy, parentMenu, /* private: */_e){
3940 this.parentMenu = parentMenu;
3945 this.fireEvent("beforeshow", this);
3946 //xy = this.el.adjustForConstraints(xy);
3950 this.hideMenuItems();
3951 this.hidden = false;
3952 if (this.triggerEl) {
3953 this.triggerEl.addClass('open');
3956 this.el.addClass('show');
3960 // reassign x when hitting right
3962 // reassign y when hitting bottom
3964 // but the list may align on trigger left or trigger top... should it be a properity?
3966 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3971 this.fireEvent("show", this);
3977 this.doFocus.defer(50, this);
3981 doFocus : function(){
3983 this.focusEl.focus();
3988 * Hides this menu and optionally all parent menus
3989 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3991 hide : function(deep)
3993 if (false === this.fireEvent("beforehide", this)) {
3994 Roo.log("hide canceled");
3997 this.hideMenuItems();
3998 if(this.el && this.isVisible()){
4000 if(this.activeItem){
4001 this.activeItem.deactivate();
4002 this.activeItem = null;
4004 if (this.triggerEl) {
4005 this.triggerEl.removeClass('open');
4008 this.el.removeClass('show');
4010 this.fireEvent("hide", this);
4012 if(deep === true && this.parentMenu){
4013 this.parentMenu.hide(true);
4017 onTriggerClick : function(e)
4019 Roo.log('trigger click');
4021 var target = e.getTarget();
4023 Roo.log(target.nodeName.toLowerCase());
4025 if(target.nodeName.toLowerCase() === 'i'){
4031 onTriggerPress : function(e)
4033 Roo.log('trigger press');
4034 //Roo.log(e.getTarget());
4035 // Roo.log(this.triggerEl.dom);
4037 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4038 var pel = Roo.get(e.getTarget());
4039 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4040 Roo.log('is treeview or dropdown?');
4044 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4048 if (this.isVisible()) {
4054 this.show(this.triggerEl, this.align, false);
4057 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4064 hideMenuItems : function()
4066 Roo.log("hide Menu Items");
4071 this.el.select('.open',true).each(function(aa) {
4073 aa.removeClass('open');
4077 addxtypeChild : function (tree, cntr) {
4078 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4080 this.menuitems.add(comp);
4092 this.getEl().dom.innerHTML = '';
4093 this.menuitems.clear();
4099 * @class Roo.bootstrap.menu.Item
4100 * @extends Roo.bootstrap.Component
4101 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4102 * @parent Roo.bootstrap.menu.Menu
4104 * Bootstrap MenuItem class
4106 * @cfg {String} html the menu label
4107 * @cfg {String} href the link
4108 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4109 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4110 * @cfg {Boolean} active used on sidebars to highlight active itesm
4111 * @cfg {String} fa favicon to show on left of menu item.
4112 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4116 * Create a new MenuItem
4117 * @param {Object} config The config object
4121 Roo.bootstrap.menu.Item = function(config){
4122 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4127 * The raw click event for the entire grid.
4128 * @param {Roo.bootstrap.menu.Item} this
4129 * @param {Roo.EventObject} e
4135 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4139 preventDefault: false,
4140 isContainer : false,
4144 getAutoCreate : function(){
4146 if(this.isContainer){
4149 cls: 'dropdown-menu-item '
4159 cls : 'dropdown-item',
4164 if (this.fa !== false) {
4167 cls : 'fa fa-' + this.fa
4176 cls: 'dropdown-menu-item',
4179 if (this.parent().type == 'treeview') {
4180 cfg.cls = 'treeview-menu';
4183 cfg.cls += ' active';
4188 anc.href = this.href || cfg.cn[0].href ;
4189 ctag.html = this.html || cfg.cn[0].html ;
4193 initEvents: function()
4195 if (this.parent().type == 'treeview') {
4196 this.el.select('a').on('click', this.onClick, this);
4200 this.menu.parentType = this.xtype;
4201 this.menu.triggerEl = this.el;
4202 this.menu = this.addxtype(Roo.apply({}, this.menu));
4206 onClick : function(e)
4208 //Roo.log('item on click ');
4210 if(this.href === false || this.preventDefault){
4213 //this.parent().hideMenuItems();
4215 this.fireEvent('click', this, e);
4229 * @class Roo.bootstrap.menu.Separator
4230 * @extends Roo.bootstrap.Component
4232 * @parent Roo.bootstrap.menu.Menu
4233 * Bootstrap Separator class
4236 * Create a new Separator
4237 * @param {Object} config The config object
4241 Roo.bootstrap.menu.Separator = function(config){
4242 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4245 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4247 getAutoCreate : function(){
4250 cls: 'dropdown-divider divider'
4266 * @class Roo.bootstrap.Modal
4267 * @extends Roo.bootstrap.Component
4268 * @parent none builder
4269 * @children Roo.bootstrap.Component
4270 * Bootstrap Modal class
4271 * @cfg {String} title Title of dialog
4272 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4273 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4274 * @cfg {Boolean} specificTitle default false
4275 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4276 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4277 * @cfg {Boolean} animate default true
4278 * @cfg {Boolean} allow_close default true
4279 * @cfg {Boolean} fitwindow default false
4280 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4281 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4282 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4283 * @cfg {String} size (sm|lg|xl) default empty
4284 * @cfg {Number} max_width set the max width of modal
4285 * @cfg {Boolean} editableTitle can the title be edited
4290 * Create a new Modal Dialog
4291 * @param {Object} config The config object
4294 Roo.bootstrap.Modal = function(config){
4295 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4300 * The raw btnclick event for the button
4301 * @param {Roo.EventObject} e
4306 * Fire when dialog resize
4307 * @param {Roo.bootstrap.Modal} this
4308 * @param {Roo.EventObject} e
4312 * @event titlechanged
4313 * Fire when the editable title has been changed
4314 * @param {Roo.bootstrap.Modal} this
4315 * @param {Roo.EventObject} value
4317 "titlechanged" : true
4320 this.buttons = this.buttons || [];
4323 this.tmpl = Roo.factory(this.tmpl);
4328 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4330 title : 'test dialog',
4340 specificTitle: false,
4342 buttonPosition: 'right',
4364 editableTitle : false,
4366 onRender : function(ct, position)
4368 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4371 var cfg = Roo.apply({}, this.getAutoCreate());
4374 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4376 //if (!cfg.name.length) {
4380 cfg.cls += ' ' + this.cls;
4383 cfg.style = this.style;
4385 this.el = Roo.get(document.body).createChild(cfg, position);
4387 //var type = this.el.dom.type;
4390 if(this.tabIndex !== undefined){
4391 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4394 this.dialogEl = this.el.select('.modal-dialog',true).first();
4395 this.bodyEl = this.el.select('.modal-body',true).first();
4396 this.closeEl = this.el.select('.modal-header .close', true).first();
4397 this.headerEl = this.el.select('.modal-header',true).first();
4398 this.titleEl = this.el.select('.modal-title',true).first();
4399 this.footerEl = this.el.select('.modal-footer',true).first();
4401 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4403 //this.el.addClass("x-dlg-modal");
4405 if (this.buttons.length) {
4406 Roo.each(this.buttons, function(bb) {
4407 var b = Roo.apply({}, bb);
4408 b.xns = b.xns || Roo.bootstrap;
4409 b.xtype = b.xtype || 'Button';
4410 if (typeof(b.listeners) == 'undefined') {
4411 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4414 var btn = Roo.factory(b);
4416 btn.render(this.getButtonContainer());
4420 // render the children.
4423 if(typeof(this.items) != 'undefined'){
4424 var items = this.items;
4427 for(var i =0;i < items.length;i++) {
4428 // we force children not to montor widnow resize - as we do that for them.
4429 items[i].monitorWindowResize = false;
4430 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4434 this.items = nitems;
4436 // where are these used - they used to be body/close/footer
4440 //this.el.addClass([this.fieldClass, this.cls]);
4444 getAutoCreate : function()
4446 // we will default to modal-body-overflow - might need to remove or make optional later.
4448 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4449 html : this.html || ''
4454 cls : 'modal-title',
4458 if(this.specificTitle){ // WTF is this?
4463 if (this.allow_close && Roo.bootstrap.version == 3) {
4473 if (this.editableTitle) {
4475 cls: 'form-control roo-editable-title d-none',
4481 if (this.allow_close && Roo.bootstrap.version == 4) {
4491 if(this.size.length){
4492 size = 'modal-' + this.size;
4495 var footer = Roo.bootstrap.version == 3 ?
4497 cls : 'modal-footer',
4501 cls: 'btn-' + this.buttonPosition
4506 { // BS4 uses mr-auto on left buttons....
4507 cls : 'modal-footer'
4518 cls: "modal-dialog " + size,
4521 cls : "modal-content",
4524 cls : 'modal-header',
4539 modal.cls += ' fade';
4545 getChildContainer : function() {
4550 getButtonContainer : function() {
4552 return Roo.bootstrap.version == 4 ?
4553 this.el.select('.modal-footer',true).first()
4554 : this.el.select('.modal-footer div',true).first();
4558 closeClick : function()
4563 initEvents : function()
4565 if (this.allow_close) {
4566 this.closeEl.on('click', this.closeClick, this);
4568 Roo.EventManager.onWindowResize(this.resize, this, true);
4569 if (this.editableTitle) {
4570 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4571 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4572 this.headerEditEl.on('keyup', function(e) {
4573 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4574 this.toggleHeaderInput(false)
4577 this.headerEditEl.on('blur', function(e) {
4578 this.toggleHeaderInput(false)
4587 this.maskEl.setSize(
4588 Roo.lib.Dom.getViewWidth(true),
4589 Roo.lib.Dom.getViewHeight(true)
4592 if (this.fitwindow) {
4594 this.dialogEl.setStyle( { 'max-width' : '100%' });
4596 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4597 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4602 if(this.max_width !== 0) {
4604 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4607 this.setSize(w, this.height);
4611 if(this.max_height) {
4612 this.setSize(w,Math.min(
4614 Roo.lib.Dom.getViewportHeight(true) - 60
4620 if(!this.fit_content) {
4621 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4625 this.setSize(w, Math.min(
4627 this.headerEl.getHeight() +
4628 this.footerEl.getHeight() +
4629 this.getChildHeight(this.bodyEl.dom.childNodes),
4630 Roo.lib.Dom.getViewportHeight(true) - 60)
4636 setSize : function(w,h)
4643 // any layout/border etc.. resize..
4645 this.items.forEach( function(e) {
4646 e.layout ? e.layout() : false;
4655 if (!this.rendered) {
4658 this.toggleHeaderInput(false);
4659 //this.el.setStyle('display', 'block');
4660 this.el.removeClass('hideing');
4661 this.el.dom.style.display='block';
4663 Roo.get(document.body).addClass('modal-open');
4665 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4668 this.el.addClass('show');
4669 this.el.addClass('in');
4672 this.el.addClass('show');
4673 this.el.addClass('in');
4676 // not sure how we can show data in here..
4678 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4681 Roo.get(document.body).addClass("x-body-masked");
4683 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4684 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4685 this.maskEl.dom.style.display = 'block';
4686 this.maskEl.addClass('show');
4691 this.fireEvent('show', this);
4693 // set zindex here - otherwise it appears to be ignored...
4694 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4697 // this is for children that are... layout.Border
4699 this.items.forEach( function(e) {
4700 e.layout ? e.layout() : false;
4708 if(this.fireEvent("beforehide", this) !== false){
4710 this.maskEl.removeClass('show');
4712 this.maskEl.dom.style.display = '';
4713 Roo.get(document.body).removeClass("x-body-masked");
4714 this.el.removeClass('in');
4715 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4717 if(this.animate){ // why
4718 this.el.addClass('hideing');
4719 this.el.removeClass('show');
4721 if (!this.el.hasClass('hideing')) {
4722 return; // it's been shown again...
4725 this.el.dom.style.display='';
4727 Roo.get(document.body).removeClass('modal-open');
4728 this.el.removeClass('hideing');
4732 this.el.removeClass('show');
4733 this.el.dom.style.display='';
4734 Roo.get(document.body).removeClass('modal-open');
4737 this.fireEvent('hide', this);
4740 isVisible : function()
4743 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4747 addButton : function(str, cb)
4751 var b = Roo.apply({}, { html : str } );
4752 b.xns = b.xns || Roo.bootstrap;
4753 b.xtype = b.xtype || 'Button';
4754 if (typeof(b.listeners) == 'undefined') {
4755 b.listeners = { click : cb.createDelegate(this) };
4758 var btn = Roo.factory(b);
4760 btn.render(this.getButtonContainer());
4766 setDefaultButton : function(btn)
4768 //this.el.select('.modal-footer').()
4771 resizeTo: function(w,h)
4773 this.dialogEl.setWidth(w);
4775 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4777 this.bodyEl.setHeight(h - diff);
4779 this.fireEvent('resize', this);
4782 setContentSize : function(w, h)
4786 onButtonClick: function(btn,e)
4789 this.fireEvent('btnclick', btn.name, e);
4792 * Set the title of the Dialog
4793 * @param {String} str new Title
4795 setTitle: function(str) {
4796 this.titleEl.dom.innerHTML = str;
4800 * Set the body of the Dialog
4801 * @param {String} str new Title
4803 setBody: function(str) {
4804 this.bodyEl.dom.innerHTML = str;
4807 * Set the body of the Dialog using the template
4808 * @param {Obj} data - apply this data to the template and replace the body contents.
4810 applyBody: function(obj)
4813 Roo.log("Error - using apply Body without a template");
4816 this.tmpl.overwrite(this.bodyEl, obj);
4819 getChildHeight : function(child_nodes)
4823 child_nodes.length == 0
4828 var child_height = 0;
4830 for(var i = 0; i < child_nodes.length; i++) {
4833 * for modal with tabs...
4834 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4836 var layout_childs = child_nodes[i].childNodes;
4838 for(var j = 0; j < layout_childs.length; j++) {
4840 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4842 var layout_body_childs = layout_childs[j].childNodes;
4844 for(var k = 0; k < layout_body_childs.length; k++) {
4846 if(layout_body_childs[k].classList.contains('navbar')) {
4847 child_height += layout_body_childs[k].offsetHeight;
4851 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4853 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4855 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4857 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4858 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4873 child_height += child_nodes[i].offsetHeight;
4874 // Roo.log(child_nodes[i].offsetHeight);
4877 return child_height;
4879 toggleHeaderInput : function(is_edit)
4881 if (!this.editableTitle) {
4882 return; // not editable.
4884 if (is_edit && this.is_header_editing) {
4885 return; // already editing..
4889 this.headerEditEl.dom.value = this.title;
4890 this.headerEditEl.removeClass('d-none');
4891 this.headerEditEl.dom.focus();
4892 this.titleEl.addClass('d-none');
4894 this.is_header_editing = true;
4897 // flip back to not editing.
4898 this.title = this.headerEditEl.dom.value;
4899 this.headerEditEl.addClass('d-none');
4900 this.titleEl.removeClass('d-none');
4901 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4902 this.is_header_editing = false;
4903 this.fireEvent('titlechanged', this, this.title);
4912 Roo.apply(Roo.bootstrap.Modal, {
4914 * Button config that displays a single OK button
4923 * Button config that displays Yes and No buttons
4939 * Button config that displays OK and Cancel buttons
4954 * Button config that displays Yes, No and Cancel buttons
4979 * messagebox - can be used as a replace
4983 * @class Roo.MessageBox
4984 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4988 Roo.Msg.alert('Status', 'Changes saved successfully.');
4990 // Prompt for user data:
4991 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4993 // process text value...
4997 // Show a dialog using config options:
4999 title:'Save Changes?',
5000 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
5001 buttons: Roo.Msg.YESNOCANCEL,
5008 Roo.bootstrap.MessageBox = function(){
5009 var dlg, opt, mask, waitTimer;
5010 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5011 var buttons, activeTextEl, bwidth;
5015 var handleButton = function(button){
5017 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5021 var handleHide = function(){
5023 dlg.el.removeClass(opt.cls);
5026 // Roo.TaskMgr.stop(waitTimer);
5027 // waitTimer = null;
5032 var updateButtons = function(b){
5035 buttons["ok"].hide();
5036 buttons["cancel"].hide();
5037 buttons["yes"].hide();
5038 buttons["no"].hide();
5039 dlg.footerEl.hide();
5043 dlg.footerEl.show();
5044 for(var k in buttons){
5045 if(typeof buttons[k] != "function"){
5048 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5049 width += buttons[k].el.getWidth()+15;
5059 var handleEsc = function(d, k, e){
5060 if(opt && opt.closable !== false){
5070 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5071 * @return {Roo.BasicDialog} The BasicDialog element
5073 getDialog : function(){
5075 dlg = new Roo.bootstrap.Modal( {
5078 //constraintoviewport:false,
5080 //collapsible : false,
5085 //buttonAlign:"center",
5086 closeClick : function(){
5087 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5090 handleButton("cancel");
5095 dlg.on("hide", handleHide);
5097 //dlg.addKeyListener(27, handleEsc);
5099 this.buttons = buttons;
5100 var bt = this.buttonText;
5101 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5102 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5103 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5104 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5106 bodyEl = dlg.bodyEl.createChild({
5108 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5109 '<textarea class="roo-mb-textarea"></textarea>' +
5110 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5112 msgEl = bodyEl.dom.firstChild;
5113 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5114 textboxEl.enableDisplayMode();
5115 textboxEl.addKeyListener([10,13], function(){
5116 if(dlg.isVisible() && opt && opt.buttons){
5119 }else if(opt.buttons.yes){
5120 handleButton("yes");
5124 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5125 textareaEl.enableDisplayMode();
5126 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5127 progressEl.enableDisplayMode();
5129 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5130 var pf = progressEl.dom.firstChild;
5132 pp = Roo.get(pf.firstChild);
5133 pp.setHeight(pf.offsetHeight);
5141 * Updates the message box body text
5142 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5143 * the XHTML-compliant non-breaking space character '&#160;')
5144 * @return {Roo.MessageBox} This message box
5146 updateText : function(text)
5148 if(!dlg.isVisible() && !opt.width){
5149 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5150 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5152 msgEl.innerHTML = text || ' ';
5154 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5155 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5157 Math.min(opt.width || cw , this.maxWidth),
5158 Math.max(opt.minWidth || this.minWidth, bwidth)
5161 activeTextEl.setWidth(w);
5163 if(dlg.isVisible()){
5164 dlg.fixedcenter = false;
5166 // to big, make it scroll. = But as usual stupid IE does not support
5169 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5170 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5171 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5173 bodyEl.dom.style.height = '';
5174 bodyEl.dom.style.overflowY = '';
5177 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5179 bodyEl.dom.style.overflowX = '';
5182 dlg.setContentSize(w, bodyEl.getHeight());
5183 if(dlg.isVisible()){
5184 dlg.fixedcenter = true;
5190 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5191 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5192 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5193 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5194 * @return {Roo.MessageBox} This message box
5196 updateProgress : function(value, text){
5198 this.updateText(text);
5201 if (pp) { // weird bug on my firefox - for some reason this is not defined
5202 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5203 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5209 * Returns true if the message box is currently displayed
5210 * @return {Boolean} True if the message box is visible, else false
5212 isVisible : function(){
5213 return dlg && dlg.isVisible();
5217 * Hides the message box if it is displayed
5220 if(this.isVisible()){
5226 * Displays a new message box, or reinitializes an existing message box, based on the config options
5227 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5228 * The following config object properties are supported:
5230 Property Type Description
5231 ---------- --------------- ------------------------------------------------------------------------------------
5232 animEl String/Element An id or Element from which the message box should animate as it opens and
5233 closes (defaults to undefined)
5234 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5235 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5236 closable Boolean False to hide the top-right close button (defaults to true). Note that
5237 progress and wait dialogs will ignore this property and always hide the
5238 close button as they can only be closed programmatically.
5239 cls String A custom CSS class to apply to the message box element
5240 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5241 displayed (defaults to 75)
5242 fn Function A callback function to execute after closing the dialog. The arguments to the
5243 function will be btn (the name of the button that was clicked, if applicable,
5244 e.g. "ok"), and text (the value of the active text field, if applicable).
5245 Progress and wait dialogs will ignore this option since they do not respond to
5246 user actions and can only be closed programmatically, so any required function
5247 should be called by the same code after it closes the dialog.
5248 icon String A CSS class that provides a background image to be used as an icon for
5249 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5250 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5251 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5252 modal Boolean False to allow user interaction with the page while the message box is
5253 displayed (defaults to true)
5254 msg String A string that will replace the existing message box body text (defaults
5255 to the XHTML-compliant non-breaking space character ' ')
5256 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5257 progress Boolean True to display a progress bar (defaults to false)
5258 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5259 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5260 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5261 title String The title text
5262 value String The string value to set into the active textbox element if displayed
5263 wait Boolean True to display a progress bar (defaults to false)
5264 width Number The width of the dialog in pixels
5271 msg: 'Please enter your address:',
5273 buttons: Roo.MessageBox.OKCANCEL,
5276 animEl: 'addAddressBtn'
5279 * @param {Object} config Configuration options
5280 * @return {Roo.MessageBox} This message box
5282 show : function(options)
5285 // this causes nightmares if you show one dialog after another
5286 // especially on callbacks..
5288 if(this.isVisible()){
5291 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5292 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5293 Roo.log("New Dialog Message:" + options.msg )
5294 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5295 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5298 var d = this.getDialog();
5300 d.setTitle(opt.title || " ");
5301 d.closeEl.setDisplayed(opt.closable !== false);
5302 activeTextEl = textboxEl;
5303 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5308 textareaEl.setHeight(typeof opt.multiline == "number" ?
5309 opt.multiline : this.defaultTextHeight);
5310 activeTextEl = textareaEl;
5319 progressEl.setDisplayed(opt.progress === true);
5321 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5323 this.updateProgress(0);
5324 activeTextEl.dom.value = opt.value || "";
5326 dlg.setDefaultButton(activeTextEl);
5328 var bs = opt.buttons;
5332 }else if(bs && bs.yes){
5333 db = buttons["yes"];
5335 dlg.setDefaultButton(db);
5337 bwidth = updateButtons(opt.buttons);
5338 this.updateText(opt.msg);
5340 d.el.addClass(opt.cls);
5342 d.proxyDrag = opt.proxyDrag === true;
5343 d.modal = opt.modal !== false;
5344 d.mask = opt.modal !== false ? mask : false;
5346 // force it to the end of the z-index stack so it gets a cursor in FF
5347 document.body.appendChild(dlg.el.dom);
5348 d.animateTarget = null;
5349 d.show(options.animEl);
5355 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5356 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5357 * and closing the message box when the process is complete.
5358 * @param {String} title The title bar text
5359 * @param {String} msg The message box body text
5360 * @return {Roo.MessageBox} This message box
5362 progress : function(title, msg){
5369 minWidth: this.minProgressWidth,
5376 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5377 * If a callback function is passed it will be called after the user clicks the button, and the
5378 * id of the button that was clicked will be passed as the only parameter to the callback
5379 * (could also be the top-right close button).
5380 * @param {String} title The title bar text
5381 * @param {String} msg The message box body text
5382 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5383 * @param {Object} scope (optional) The scope of the callback function
5384 * @return {Roo.MessageBox} This message box
5386 alert : function(title, msg, fn, scope)
5401 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5402 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5403 * You are responsible for closing the message box when the process is complete.
5404 * @param {String} msg The message box body text
5405 * @param {String} title (optional) The title bar text
5406 * @return {Roo.MessageBox} This message box
5408 wait : function(msg, title){
5419 waitTimer = Roo.TaskMgr.start({
5421 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5429 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5430 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5431 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5432 * @param {String} title The title bar text
5433 * @param {String} msg The message box body text
5434 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5435 * @param {Object} scope (optional) The scope of the callback function
5436 * @return {Roo.MessageBox} This message box
5438 confirm : function(title, msg, fn, scope){
5442 buttons: this.YESNO,
5451 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5452 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5453 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5454 * (could also be the top-right close button) and the text that was entered will be passed as the two
5455 * parameters to the callback.
5456 * @param {String} title The title bar text
5457 * @param {String} msg The message box body text
5458 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5459 * @param {Object} scope (optional) The scope of the callback function
5460 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5461 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5462 * @return {Roo.MessageBox} This message box
5464 prompt : function(title, msg, fn, scope, multiline){
5468 buttons: this.OKCANCEL,
5473 multiline: multiline,
5480 * Button config that displays a single OK button
5485 * Button config that displays Yes and No buttons
5488 YESNO : {yes:true, no:true},
5490 * Button config that displays OK and Cancel buttons
5493 OKCANCEL : {ok:true, cancel:true},
5495 * Button config that displays Yes, No and Cancel buttons
5498 YESNOCANCEL : {yes:true, no:true, cancel:true},
5501 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5504 defaultTextHeight : 75,
5506 * The maximum width in pixels of the message box (defaults to 600)
5511 * The minimum width in pixels of the message box (defaults to 100)
5516 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5517 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5520 minProgressWidth : 250,
5522 * An object containing the default button text strings that can be overriden for localized language support.
5523 * Supported properties are: ok, cancel, yes and no.
5524 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5537 * Shorthand for {@link Roo.MessageBox}
5539 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5540 Roo.Msg = Roo.Msg || Roo.MessageBox;
5549 * @class Roo.bootstrap.nav.Bar
5550 * @extends Roo.bootstrap.Component
5552 * Bootstrap Navbar class
5555 * Create a new Navbar
5556 * @param {Object} config The config object
5560 Roo.bootstrap.nav.Bar = function(config){
5561 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5565 * @event beforetoggle
5566 * Fire before toggle the menu
5567 * @param {Roo.EventObject} e
5569 "beforetoggle" : true
5573 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5582 getAutoCreate : function(){
5585 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5589 initEvents :function ()
5591 //Roo.log(this.el.select('.navbar-toggle',true));
5592 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5599 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5601 var size = this.el.getSize();
5602 this.maskEl.setSize(size.width, size.height);
5603 this.maskEl.enableDisplayMode("block");
5612 getChildContainer : function()
5614 if (this.el && this.el.select('.collapse').getCount()) {
5615 return this.el.select('.collapse',true).first();
5630 onToggle : function()
5633 if(this.fireEvent('beforetoggle', this) === false){
5636 var ce = this.el.select('.navbar-collapse',true).first();
5638 if (!ce.hasClass('show')) {
5648 * Expand the navbar pulldown
5650 expand : function ()
5653 var ce = this.el.select('.navbar-collapse',true).first();
5654 if (ce.hasClass('collapsing')) {
5657 ce.dom.style.height = '';
5659 ce.addClass('in'); // old...
5660 ce.removeClass('collapse');
5661 ce.addClass('show');
5662 var h = ce.getHeight();
5664 ce.removeClass('show');
5665 // at this point we should be able to see it..
5666 ce.addClass('collapsing');
5668 ce.setHeight(0); // resize it ...
5669 ce.on('transitionend', function() {
5670 //Roo.log('done transition');
5671 ce.removeClass('collapsing');
5672 ce.addClass('show');
5673 ce.removeClass('collapse');
5675 ce.dom.style.height = '';
5676 }, this, { single: true} );
5678 ce.dom.scrollTop = 0;
5681 * Collapse the navbar pulldown
5683 collapse : function()
5685 var ce = this.el.select('.navbar-collapse',true).first();
5687 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5688 // it's collapsed or collapsing..
5691 ce.removeClass('in'); // old...
5692 ce.setHeight(ce.getHeight());
5693 ce.removeClass('show');
5694 ce.addClass('collapsing');
5696 ce.on('transitionend', function() {
5697 ce.dom.style.height = '';
5698 ce.removeClass('collapsing');
5699 ce.addClass('collapse');
5700 }, this, { single: true} );
5720 * @class Roo.bootstrap.nav.Simplebar
5721 * @extends Roo.bootstrap.nav.Bar
5722 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5723 * Bootstrap Sidebar class
5725 * @cfg {Boolean} inverse is inverted color
5727 * @cfg {String} type (nav | pills | tabs)
5728 * @cfg {Boolean} arrangement stacked | justified
5729 * @cfg {String} align (left | right) alignment
5731 * @cfg {Boolean} main (true|false) main nav bar? default false
5732 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5734 * @cfg {String} tag (header|footer|nav|div) default is nav
5736 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5740 * Create a new Sidebar
5741 * @param {Object} config The config object
5745 Roo.bootstrap.nav.Simplebar = function(config){
5746 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5749 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5765 getAutoCreate : function(){
5769 tag : this.tag || 'div',
5770 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5772 if (['light','white'].indexOf(this.weight) > -1) {
5773 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5775 cfg.cls += ' bg-' + this.weight;
5778 cfg.cls += ' navbar-inverse';
5782 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5784 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5793 cls: 'nav nav-' + this.xtype,
5799 this.type = this.type || 'nav';
5800 if (['tabs','pills'].indexOf(this.type) != -1) {
5801 cfg.cn[0].cls += ' nav-' + this.type
5805 if (this.type!=='nav') {
5806 Roo.log('nav type must be nav/tabs/pills')
5808 cfg.cn[0].cls += ' navbar-nav'
5814 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5815 cfg.cn[0].cls += ' nav-' + this.arrangement;
5819 if (this.align === 'right') {
5820 cfg.cn[0].cls += ' navbar-right';
5845 * navbar-expand-md fixed-top
5849 * @class Roo.bootstrap.nav.Headerbar
5850 * @extends Roo.bootstrap.nav.Simplebar
5851 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5852 * Bootstrap Sidebar class
5854 * @cfg {String} brand what is brand
5855 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5856 * @cfg {String} brand_href href of the brand
5857 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5858 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5859 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5860 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5863 * Create a new Sidebar
5864 * @param {Object} config The config object
5868 Roo.bootstrap.nav.Headerbar = function(config){
5869 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5873 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5880 desktopCenter : false,
5883 getAutoCreate : function(){
5886 tag: this.nav || 'nav',
5887 cls: 'navbar navbar-expand-md',
5893 if (this.desktopCenter) {
5894 cn.push({cls : 'container', cn : []});
5902 cls: 'navbar-toggle navbar-toggler',
5903 'data-toggle': 'collapse',
5908 html: 'Toggle navigation'
5912 cls: 'icon-bar navbar-toggler-icon'
5925 cn.push( Roo.bootstrap.version == 4 ? btn : {
5927 cls: 'navbar-header',
5936 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5940 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5942 if (['light','white'].indexOf(this.weight) > -1) {
5943 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5945 cfg.cls += ' bg-' + this.weight;
5948 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5949 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5951 // tag can override this..
5953 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5956 if (this.brand !== '') {
5957 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5958 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5960 href: this.brand_href ? this.brand_href : '#',
5961 cls: 'navbar-brand',
5969 cfg.cls += ' main-nav';
5977 getHeaderChildContainer : function()
5979 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5980 return this.el.select('.navbar-header',true).first();
5983 return this.getChildContainer();
5986 getChildContainer : function()
5989 return this.el.select('.roo-navbar-collapse',true).first();
5994 initEvents : function()
5996 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5998 if (this.autohide) {
6003 Roo.get(document).on('scroll',function(e) {
6004 var ns = Roo.get(document).getScroll().top;
6005 var os = prevScroll;
6009 ft.removeClass('slideDown');
6010 ft.addClass('slideUp');
6013 ft.removeClass('slideUp');
6014 ft.addClass('slideDown');
6035 * @class Roo.bootstrap.nav.Sidebar
6036 * @extends Roo.bootstrap.nav.Bar
6037 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6038 * Bootstrap Sidebar class
6041 * Create a new Sidebar
6042 * @param {Object} config The config object
6046 Roo.bootstrap.nav.Sidebar = function(config){
6047 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6050 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6052 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6054 getAutoCreate : function(){
6059 cls: 'sidebar sidebar-nav'
6081 * @class Roo.bootstrap.nav.Group
6082 * @extends Roo.bootstrap.Component
6083 * @children Roo.bootstrap.nav.Item
6084 * Bootstrap NavGroup class
6085 * @cfg {String} align (left|right)
6086 * @cfg {Boolean} inverse
6087 * @cfg {String} type (nav|pills|tab) default nav
6088 * @cfg {String} navId - reference Id for navbar.
6089 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6092 * Create a new nav group
6093 * @param {Object} config The config object
6096 Roo.bootstrap.nav.Group = function(config){
6097 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6100 Roo.bootstrap.nav.Group.register(this);
6104 * Fires when the active item changes
6105 * @param {Roo.bootstrap.nav.Group} this
6106 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6107 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6114 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6126 getAutoCreate : function()
6128 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6134 if (Roo.bootstrap.version == 4) {
6135 if (['tabs','pills'].indexOf(this.type) != -1) {
6136 cfg.cls += ' nav-' + this.type;
6138 // trying to remove so header bar can right align top?
6139 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6140 // do not use on header bar...
6141 cfg.cls += ' navbar-nav';
6146 if (['tabs','pills'].indexOf(this.type) != -1) {
6147 cfg.cls += ' nav-' + this.type
6149 if (this.type !== 'nav') {
6150 Roo.log('nav type must be nav/tabs/pills')
6152 cfg.cls += ' navbar-nav'
6156 if (this.parent() && this.parent().sidebar) {
6159 cls: 'dashboard-menu sidebar-menu'
6165 if (this.form === true) {
6168 cls: 'navbar-form form-inline'
6170 //nav navbar-right ml-md-auto
6171 if (this.align === 'right') {
6172 cfg.cls += ' navbar-right ml-md-auto';
6174 cfg.cls += ' navbar-left';
6178 if (this.align === 'right') {
6179 cfg.cls += ' navbar-right ml-md-auto';
6181 cfg.cls += ' mr-auto';
6185 cfg.cls += ' navbar-inverse';
6193 * sets the active Navigation item
6194 * @param {Roo.bootstrap.nav.Item} the new current navitem
6196 setActiveItem : function(item)
6199 Roo.each(this.navItems, function(v){
6204 v.setActive(false, true);
6211 item.setActive(true, true);
6212 this.fireEvent('changed', this, item, prev);
6217 * gets the active Navigation item
6218 * @return {Roo.bootstrap.nav.Item} the current navitem
6220 getActive : function()
6224 Roo.each(this.navItems, function(v){
6235 indexOfNav : function()
6239 Roo.each(this.navItems, function(v,i){
6250 * adds a Navigation item
6251 * @param {Roo.bootstrap.nav.Item} the navitem to add
6253 addItem : function(cfg)
6255 if (this.form && Roo.bootstrap.version == 4) {
6258 var cn = new Roo.bootstrap.nav.Item(cfg);
6260 cn.parentId = this.id;
6261 cn.onRender(this.el, null);
6265 * register a Navigation item
6266 * @param {Roo.bootstrap.nav.Item} the navitem to add
6268 register : function(item)
6270 this.navItems.push( item);
6271 item.navId = this.navId;
6276 * clear all the Navigation item
6279 clearAll : function()
6282 this.el.dom.innerHTML = '';
6285 getNavItem: function(tabId)
6288 Roo.each(this.navItems, function(e) {
6289 if (e.tabId == tabId) {
6299 setActiveNext : function()
6301 var i = this.indexOfNav(this.getActive());
6302 if (i > this.navItems.length) {
6305 this.setActiveItem(this.navItems[i+1]);
6307 setActivePrev : function()
6309 var i = this.indexOfNav(this.getActive());
6313 this.setActiveItem(this.navItems[i-1]);
6315 clearWasActive : function(except) {
6316 Roo.each(this.navItems, function(e) {
6317 if (e.tabId != except.tabId && e.was_active) {
6318 e.was_active = false;
6325 getWasActive : function ()
6328 Roo.each(this.navItems, function(e) {
6343 Roo.apply(Roo.bootstrap.nav.Group, {
6347 * register a Navigation Group
6348 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6350 register : function(navgrp)
6352 this.groups[navgrp.navId] = navgrp;
6356 * fetch a Navigation Group based on the navigation ID
6357 * @param {string} the navgroup to add
6358 * @returns {Roo.bootstrap.nav.Group} the navgroup
6360 get: function(navId) {
6361 if (typeof(this.groups[navId]) == 'undefined') {
6363 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6365 return this.groups[navId] ;
6373 * @class Roo.bootstrap.nav.Item
6374 * @extends Roo.bootstrap.Component
6375 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6376 * @parent Roo.bootstrap.nav.Group
6378 * Bootstrap Navbar.NavItem class
6380 * @cfg {String} href link to
6381 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6382 * @cfg {Boolean} button_outline show and outlined button
6383 * @cfg {String} html content of button
6384 * @cfg {String} badge text inside badge
6385 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6386 * @cfg {String} glyphicon DEPRICATED - use fa
6387 * @cfg {String} icon DEPRICATED - use fa
6388 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6389 * @cfg {Boolean} active Is item active
6390 * @cfg {Boolean} disabled Is item disabled
6391 * @cfg {String} linkcls Link Class
6392 * @cfg {Boolean} preventDefault (true | false) default false
6393 * @cfg {String} tabId the tab that this item activates.
6394 * @cfg {String} tagtype (a|span) render as a href or span?
6395 * @cfg {Boolean} animateRef (true|false) link to element default false
6396 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6399 * Create a new Navbar Item
6400 * @param {Object} config The config object
6402 Roo.bootstrap.nav.Item = function(config){
6403 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6408 * The raw click event for the entire grid.
6409 * @param {Roo.EventObject} e
6414 * Fires when the active item active state changes
6415 * @param {Roo.bootstrap.nav.Item} this
6416 * @param {boolean} state the new state
6422 * Fires when scroll to element
6423 * @param {Roo.bootstrap.nav.Item} this
6424 * @param {Object} options
6425 * @param {Roo.EventObject} e
6433 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6442 preventDefault : false,
6450 button_outline : false,
6454 getAutoCreate : function(){
6461 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6464 cfg.cls += ' active' ;
6466 if (this.disabled) {
6467 cfg.cls += ' disabled';
6471 if (this.button_weight.length) {
6472 cfg.tag = this.href ? 'a' : 'button';
6473 cfg.html = this.html || '';
6474 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6476 cfg.href = this.href;
6479 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6481 cfg.cls += " nav-html";
6484 // menu .. should add dropdown-menu class - so no need for carat..
6486 if (this.badge !== '') {
6488 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6493 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6497 href : this.href || "#",
6498 html: this.html || '',
6502 if (this.tagtype == 'a') {
6503 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6507 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6508 } else if (this.fa) {
6509 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6510 } else if(this.glyphicon) {
6511 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6513 cfg.cn[0].cls += " nav-html";
6517 cfg.cn[0].html += " <span class='caret'></span>";
6521 if (this.badge !== '') {
6522 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6530 onRender : function(ct, position)
6532 // Roo.log("Call onRender: " + this.xtype);
6533 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6537 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6538 this.navLink = this.el.select('.nav-link',true).first();
6539 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6544 initEvents: function()
6546 if (typeof (this.menu) != 'undefined') {
6547 this.menu.parentType = this.xtype;
6548 this.menu.triggerEl = this.el;
6549 this.menu = this.addxtype(Roo.apply({}, this.menu));
6552 this.el.on('click', this.onClick, this);
6554 //if(this.tagtype == 'span'){
6555 // this.el.select('span',true).on('click', this.onClick, this);
6558 // at this point parent should be available..
6559 this.parent().register(this);
6562 onClick : function(e)
6564 if (e.getTarget('.dropdown-menu-item')) {
6565 // did you click on a menu itemm.... - then don't trigger onclick..
6570 this.preventDefault ||
6571 this.href === false ||
6574 //Roo.log("NavItem - prevent Default?");
6578 if (this.disabled) {
6582 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6583 if (tg && tg.transition) {
6584 Roo.log("waiting for the transitionend");
6590 //Roo.log("fire event clicked");
6591 if(this.fireEvent('click', this, e) === false){
6595 if(this.tagtype == 'span'){
6599 //Roo.log(this.href);
6600 var ael = this.el.select('a',true).first();
6603 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6604 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6605 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6606 return; // ignore... - it's a 'hash' to another page.
6608 Roo.log("NavItem - prevent Default?");
6610 this.scrollToElement(e);
6614 var p = this.parent();
6616 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6617 if (typeof(p.setActiveItem) !== 'undefined') {
6618 p.setActiveItem(this);
6622 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6623 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6624 // remove the collapsed menu expand...
6625 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6629 isActive: function () {
6632 setActive : function(state, fire, is_was_active)
6634 if (this.active && !state && this.navId) {
6635 this.was_active = true;
6636 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6638 nv.clearWasActive(this);
6642 this.active = state;
6645 this.el.removeClass('active');
6646 this.navLink ? this.navLink.removeClass('active') : false;
6647 } else if (!this.el.hasClass('active')) {
6649 this.el.addClass('active');
6650 if (Roo.bootstrap.version == 4 && this.navLink ) {
6651 this.navLink.addClass('active');
6656 this.fireEvent('changed', this, state);
6659 // show a panel if it's registered and related..
6661 if (!this.navId || !this.tabId || !state || is_was_active) {
6665 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6669 var pan = tg.getPanelByName(this.tabId);
6673 // if we can not flip to new panel - go back to old nav highlight..
6674 if (false == tg.showPanel(pan)) {
6675 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6677 var onav = nv.getWasActive();
6679 onav.setActive(true, false, true);
6688 // this should not be here...
6689 setDisabled : function(state)
6691 this.disabled = state;
6693 this.el.removeClass('disabled');
6694 } else if (!this.el.hasClass('disabled')) {
6695 this.el.addClass('disabled');
6701 * Fetch the element to display the tooltip on.
6702 * @return {Roo.Element} defaults to this.el
6704 tooltipEl : function()
6706 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6709 scrollToElement : function(e)
6711 var c = document.body;
6714 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6716 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6717 c = document.documentElement;
6720 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6726 var o = target.calcOffsetsTo(c);
6733 this.fireEvent('scrollto', this, options, e);
6735 Roo.get(c).scrollTo('top', options.value, true);
6740 * Set the HTML (text content) of the item
6741 * @param {string} html content for the nav item
6743 setHtml : function(html)
6746 this.htmlEl.dom.innerHTML = html;
6758 * <span> icon </span>
6759 * <span> text </span>
6760 * <span>badge </span>
6764 * @class Roo.bootstrap.nav.SidebarItem
6765 * @extends Roo.bootstrap.nav.Item
6766 * Bootstrap Navbar.NavSidebarItem class
6768 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6769 * {Boolean} open is the menu open
6770 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6771 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6772 * {String} buttonSize (sm|md|lg)the extra classes for the button
6773 * {Boolean} showArrow show arrow next to the text (default true)
6775 * Create a new Navbar Button
6776 * @param {Object} config The config object
6778 Roo.bootstrap.nav.SidebarItem = function(config){
6779 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6784 * The raw click event for the entire grid.
6785 * @param {Roo.EventObject} e
6790 * Fires when the active item active state changes
6791 * @param {Roo.bootstrap.nav.SidebarItem} this
6792 * @param {boolean} state the new state
6800 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6802 badgeWeight : 'default',
6808 buttonWeight : 'default',
6814 getAutoCreate : function(){
6819 href : this.href || '#',
6825 if(this.buttonView){
6828 href : this.href || '#',
6829 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6842 cfg.cls += ' active';
6845 if (this.disabled) {
6846 cfg.cls += ' disabled';
6849 cfg.cls += ' open x-open';
6852 if (this.glyphicon || this.icon) {
6853 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6854 a.cn.push({ tag : 'i', cls : c }) ;
6857 if(!this.buttonView){
6860 html : this.html || ''
6867 if (this.badge !== '') {
6868 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6874 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6877 a.cls += ' dropdown-toggle treeview' ;
6883 initEvents : function()
6885 if (typeof (this.menu) != 'undefined') {
6886 this.menu.parentType = this.xtype;
6887 this.menu.triggerEl = this.el;
6888 this.menu = this.addxtype(Roo.apply({}, this.menu));
6891 this.el.on('click', this.onClick, this);
6893 if(this.badge !== ''){
6894 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6899 onClick : function(e)
6906 if(this.preventDefault){
6910 this.fireEvent('click', this, e);
6913 disable : function()
6915 this.setDisabled(true);
6920 this.setDisabled(false);
6923 setDisabled : function(state)
6925 if(this.disabled == state){
6929 this.disabled = state;
6932 this.el.addClass('disabled');
6936 this.el.removeClass('disabled');
6941 setActive : function(state)
6943 if(this.active == state){
6947 this.active = state;
6950 this.el.addClass('active');
6954 this.el.removeClass('active');
6959 isActive: function ()
6964 setBadge : function(str)
6970 this.badgeEl.dom.innerHTML = str;
6987 * @class Roo.bootstrap.nav.ProgressBar
6988 * @extends Roo.bootstrap.Component
6989 * @children Roo.bootstrap.nav.ProgressBarItem
6990 * Bootstrap NavProgressBar class
6993 * Create a new nav progress bar - a bar indicating step along a process
6994 * @param {Object} config The config object
6997 Roo.bootstrap.nav.ProgressBar = function(config){
6998 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
7000 this.bullets = this.bullets || [];
7002 // Roo.bootstrap.nav.ProgressBar.register(this);
7006 * Fires when the active item changes
7007 * @param {Roo.bootstrap.nav.ProgressBar} this
7008 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7009 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7016 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7018 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7019 * Bullets for the Nav Progress bar for the toolbar
7024 getAutoCreate : function()
7026 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7030 cls : 'roo-navigation-bar-group',
7034 cls : 'roo-navigation-top-bar'
7038 cls : 'roo-navigation-bullets-bar',
7042 cls : 'roo-navigation-bar'
7049 cls : 'roo-navigation-bottom-bar'
7059 initEvents: function()
7064 onRender : function(ct, position)
7066 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7068 if(this.bullets.length){
7069 Roo.each(this.bullets, function(b){
7078 addItem : function(cfg)
7080 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7082 item.parentId = this.id;
7083 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7086 var top = new Roo.bootstrap.Element({
7088 cls : 'roo-navigation-bar-text'
7091 var bottom = new Roo.bootstrap.Element({
7093 cls : 'roo-navigation-bar-text'
7096 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7097 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7099 var topText = new Roo.bootstrap.Element({
7101 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7104 var bottomText = new Roo.bootstrap.Element({
7106 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7109 topText.onRender(top.el, null);
7110 bottomText.onRender(bottom.el, null);
7113 item.bottomEl = bottom;
7116 this.barItems.push(item);
7121 getActive : function()
7125 Roo.each(this.barItems, function(v){
7127 if (!v.isActive()) {
7139 setActiveItem : function(item)
7143 Roo.each(this.barItems, function(v){
7144 if (v.rid == item.rid) {
7154 item.setActive(true);
7156 this.fireEvent('changed', this, item, prev);
7159 getBarItem: function(rid)
7163 Roo.each(this.barItems, function(e) {
7175 indexOfItem : function(item)
7179 Roo.each(this.barItems, function(v, i){
7181 if (v.rid != item.rid) {
7192 setActiveNext : function()
7194 var i = this.indexOfItem(this.getActive());
7196 if (i > this.barItems.length) {
7200 this.setActiveItem(this.barItems[i+1]);
7203 setActivePrev : function()
7205 var i = this.indexOfItem(this.getActive());
7211 this.setActiveItem(this.barItems[i-1]);
7216 if(!this.barItems.length){
7220 var width = 100 / this.barItems.length;
7222 Roo.each(this.barItems, function(i){
7223 i.el.setStyle('width', width + '%');
7224 i.topEl.el.setStyle('width', width + '%');
7225 i.bottomEl.el.setStyle('width', width + '%');
7239 * @class Roo.bootstrap.nav.ProgressBarItem
7240 * @extends Roo.bootstrap.Component
7241 * Bootstrap NavProgressBarItem class
7242 * @cfg {String} rid the reference id
7243 * @cfg {Boolean} active (true|false) Is item active default false
7244 * @cfg {Boolean} disabled (true|false) Is item active default false
7245 * @cfg {String} html
7246 * @cfg {String} position (top|bottom) text position default bottom
7247 * @cfg {String} icon show icon instead of number
7250 * Create a new NavProgressBarItem
7251 * @param {Object} config The config object
7253 Roo.bootstrap.nav.ProgressBarItem = function(config){
7254 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7259 * The raw click event for the entire grid.
7260 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7261 * @param {Roo.EventObject} e
7268 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7274 position : 'bottom',
7277 getAutoCreate : function()
7279 var iconCls = 'roo-navigation-bar-item-icon';
7281 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7285 cls: 'roo-navigation-bar-item',
7295 cfg.cls += ' active';
7298 cfg.cls += ' disabled';
7304 disable : function()
7306 this.setDisabled(true);
7311 this.setDisabled(false);
7314 initEvents: function()
7316 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7318 this.iconEl.on('click', this.onClick, this);
7321 onClick : function(e)
7329 if(this.fireEvent('click', this, e) === false){
7333 this.parent().setActiveItem(this);
7336 isActive: function ()
7341 setActive : function(state)
7343 if(this.active == state){
7347 this.active = state;
7350 this.el.addClass('active');
7354 this.el.removeClass('active');
7359 setDisabled : function(state)
7361 if(this.disabled == state){
7365 this.disabled = state;
7368 this.el.addClass('disabled');
7372 this.el.removeClass('disabled');
7375 tooltipEl : function()
7377 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7388 Roo.namespace('Roo.bootstrap.breadcrumb');
7392 * @class Roo.bootstrap.breadcrumb.Nav
7393 * @extends Roo.bootstrap.Component
7394 * Bootstrap Breadcrumb Nav Class
7396 * @children Roo.bootstrap.breadcrumb.Item
7399 * Create a new breadcrumb.Nav
7400 * @param {Object} config The config object
7404 Roo.bootstrap.breadcrumb.Nav = function(config){
7405 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7410 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7412 getAutoCreate : function()
7429 initEvents: function()
7431 this.olEl = this.el.select('ol',true).first();
7433 getChildContainer : function()
7449 * @class Roo.bootstrap.breadcrumb.Nav
7450 * @extends Roo.bootstrap.Component
7451 * @children Roo.bootstrap.Component
7452 * @parent Roo.bootstrap.breadcrumb.Nav
7453 * Bootstrap Breadcrumb Nav Class
7456 * @cfg {String} html the content of the link.
7457 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7458 * @cfg {Boolean} active is it active
7462 * Create a new breadcrumb.Nav
7463 * @param {Object} config The config object
7466 Roo.bootstrap.breadcrumb.Item = function(config){
7467 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7472 * The img click event for the img.
7473 * @param {Roo.EventObject} e
7480 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7485 getAutoCreate : function()
7490 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7492 if (this.href !== false) {
7499 cfg.html = this.html;
7505 initEvents: function()
7508 this.el.select('a', true).first().on('click',this.onClick, this)
7512 onClick : function(e)
7515 this.fireEvent('click',this, e);
7528 * @class Roo.bootstrap.Row
7529 * @extends Roo.bootstrap.Component
7530 * @children Roo.bootstrap.Component
7531 * Bootstrap Row class (contains columns...)
7535 * @param {Object} config The config object
7538 Roo.bootstrap.Row = function(config){
7539 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7542 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7544 getAutoCreate : function(){
7563 * @class Roo.bootstrap.Pagination
7564 * @extends Roo.bootstrap.Component
7565 * @children Roo.bootstrap.Pagination
7566 * Bootstrap Pagination class
7568 * @cfg {String} size (xs|sm|md|lg|xl)
7569 * @cfg {Boolean} inverse
7572 * Create a new Pagination
7573 * @param {Object} config The config object
7576 Roo.bootstrap.Pagination = function(config){
7577 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7580 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7586 getAutoCreate : function(){
7592 cfg.cls += ' inverse';
7598 cfg.cls += " " + this.cls;
7616 * @class Roo.bootstrap.PaginationItem
7617 * @extends Roo.bootstrap.Component
7618 * Bootstrap PaginationItem class
7619 * @cfg {String} html text
7620 * @cfg {String} href the link
7621 * @cfg {Boolean} preventDefault (true | false) default true
7622 * @cfg {Boolean} active (true | false) default false
7623 * @cfg {Boolean} disabled default false
7627 * Create a new PaginationItem
7628 * @param {Object} config The config object
7632 Roo.bootstrap.PaginationItem = function(config){
7633 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7638 * The raw click event for the entire grid.
7639 * @param {Roo.EventObject} e
7645 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7649 preventDefault: true,
7654 getAutoCreate : function(){
7660 href : this.href ? this.href : '#',
7661 html : this.html ? this.html : ''
7671 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7675 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7681 initEvents: function() {
7683 this.el.on('click', this.onClick, this);
7686 onClick : function(e)
7688 Roo.log('PaginationItem on click ');
7689 if(this.preventDefault){
7697 this.fireEvent('click', this, e);
7713 * @class Roo.bootstrap.Slider
7714 * @extends Roo.bootstrap.Component
7715 * Bootstrap Slider class
7718 * Create a new Slider
7719 * @param {Object} config The config object
7722 Roo.bootstrap.Slider = function(config){
7723 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7726 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7728 getAutoCreate : function(){
7732 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7736 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7748 * Ext JS Library 1.1.1
7749 * Copyright(c) 2006-2007, Ext JS, LLC.
7751 * Originally Released Under LGPL - original licence link has changed is not relivant.
7754 * <script type="text/javascript">
7757 * @extends Roo.dd.DDProxy
7758 * @class Roo.grid.SplitDragZone
7759 * Support for Column Header resizing
7761 * @param {Object} config
7764 // This is a support class used internally by the Grid components
7765 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7767 this.view = grid.getView();
7768 this.proxy = this.view.resizeProxy;
7769 Roo.grid.SplitDragZone.superclass.constructor.call(
7772 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7774 dragElId : Roo.id(this.proxy.dom),
7779 this.setHandleElId(Roo.id(hd));
7780 if (hd2 !== false) {
7781 this.setOuterHandleElId(Roo.id(hd2));
7784 this.scroll = false;
7786 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7787 fly: Roo.Element.fly,
7789 b4StartDrag : function(x, y){
7790 this.view.headersDisabled = true;
7791 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7792 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7794 this.proxy.setHeight(h);
7796 // for old system colWidth really stored the actual width?
7797 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7798 // which in reality did not work.. - it worked only for fixed sizes
7799 // for resizable we need to use actual sizes.
7800 var w = this.cm.getColumnWidth(this.cellIndex);
7801 if (!this.view.mainWrap) {
7803 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7808 // this was w-this.grid.minColumnWidth;
7809 // doesnt really make sense? - w = thie curren width or the rendered one?
7810 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7811 this.resetConstraints();
7812 this.setXConstraint(minw, 1000);
7813 this.setYConstraint(0, 0);
7814 this.minX = x - minw;
7815 this.maxX = x + 1000;
7817 if (!this.view.mainWrap) { // this is Bootstrap code..
7818 this.getDragEl().style.display='block';
7821 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7825 handleMouseDown : function(e){
7826 ev = Roo.EventObject.setEvent(e);
7827 var t = this.fly(ev.getTarget());
7828 if(t.hasClass("x-grid-split")){
7829 this.cellIndex = this.view.getCellIndex(t.dom);
7831 this.cm = this.grid.colModel;
7832 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7833 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7838 endDrag : function(e){
7839 this.view.headersDisabled = false;
7840 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7841 var diff = endX - this.startPos;
7843 var w = this.cm.getColumnWidth(this.cellIndex);
7844 if (!this.view.mainWrap) {
7847 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7850 autoOffset : function(){
7855 * Ext JS Library 1.1.1
7856 * Copyright(c) 2006-2007, Ext JS, LLC.
7858 * Originally Released Under LGPL - original licence link has changed is not relivant.
7861 * <script type="text/javascript">
7865 * @class Roo.grid.AbstractSelectionModel
7866 * @extends Roo.util.Observable
7868 * Abstract base class for grid SelectionModels. It provides the interface that should be
7869 * implemented by descendant classes. This class should not be directly instantiated.
7872 Roo.grid.AbstractSelectionModel = function(){
7873 this.locked = false;
7874 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7877 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7878 /** @ignore Called by the grid automatically. Do not call directly. */
7879 init : function(grid){
7885 * Locks the selections.
7892 * Unlocks the selections.
7894 unlock : function(){
7895 this.locked = false;
7899 * Returns true if the selections are locked.
7902 isLocked : function(){
7907 * Ext JS Library 1.1.1
7908 * Copyright(c) 2006-2007, Ext JS, LLC.
7910 * Originally Released Under LGPL - original licence link has changed is not relivant.
7913 * <script type="text/javascript">
7916 * @extends Roo.grid.AbstractSelectionModel
7917 * @class Roo.grid.RowSelectionModel
7918 * The default SelectionModel used by {@link Roo.grid.Grid}.
7919 * It supports multiple selections and keyboard selection/navigation.
7921 * @param {Object} config
7923 Roo.grid.RowSelectionModel = function(config){
7924 Roo.apply(this, config);
7925 this.selections = new Roo.util.MixedCollection(false, function(o){
7930 this.lastActive = false;
7934 * @event selectionchange
7935 * Fires when the selection changes
7936 * @param {SelectionModel} this
7938 "selectionchange" : true,
7940 * @event afterselectionchange
7941 * Fires after the selection changes (eg. by key press or clicking)
7942 * @param {SelectionModel} this
7944 "afterselectionchange" : true,
7946 * @event beforerowselect
7947 * Fires when a row is selected being selected, return false to cancel.
7948 * @param {SelectionModel} this
7949 * @param {Number} rowIndex The selected index
7950 * @param {Boolean} keepExisting False if other selections will be cleared
7952 "beforerowselect" : true,
7955 * Fires when a row is selected.
7956 * @param {SelectionModel} this
7957 * @param {Number} rowIndex The selected index
7958 * @param {Roo.data.Record} r The record
7962 * @event rowdeselect
7963 * Fires when a row is deselected.
7964 * @param {SelectionModel} this
7965 * @param {Number} rowIndex The selected index
7967 "rowdeselect" : true
7969 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7970 this.locked = false;
7973 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7975 * @cfg {Boolean} singleSelect
7976 * True to allow selection of only one row at a time (defaults to false)
7978 singleSelect : false,
7981 initEvents : function(){
7983 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7984 this.grid.on("mousedown", this.handleMouseDown, this);
7985 }else{ // allow click to work like normal
7986 this.grid.on("rowclick", this.handleDragableRowClick, this);
7988 // bootstrap does not have a view..
7989 var view = this.grid.view ? this.grid.view : this.grid;
7990 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7993 this.selectPrevious(e.shiftKey);
7994 }else if(this.last !== false && this.lastActive !== false){
7995 var last = this.last;
7996 this.selectRange(this.last, this.lastActive-1);
7997 view.focusRow(this.lastActive);
8002 this.selectFirstRow();
8004 this.fireEvent("afterselectionchange", this);
8006 "down" : function(e){
8008 this.selectNext(e.shiftKey);
8009 }else if(this.last !== false && this.lastActive !== false){
8010 var last = this.last;
8011 this.selectRange(this.last, this.lastActive+1);
8012 view.focusRow(this.lastActive);
8017 this.selectFirstRow();
8019 this.fireEvent("afterselectionchange", this);
8025 view.on("refresh", this.onRefresh, this);
8026 view.on("rowupdated", this.onRowUpdated, this);
8027 view.on("rowremoved", this.onRemove, this);
8031 onRefresh : function(){
8032 var ds = this.grid.ds, i, v = this.grid.view;
8033 var s = this.selections;
8035 if((i = ds.indexOfId(r.id)) != -1){
8037 s.add(ds.getAt(i)); // updating the selection relate data
8045 onRemove : function(v, index, r){
8046 this.selections.remove(r);
8050 onRowUpdated : function(v, index, r){
8051 if(this.isSelected(r)){
8052 v.onRowSelect(index);
8058 * @param {Array} records The records to select
8059 * @param {Boolean} keepExisting (optional) True to keep existing selections
8061 selectRecords : function(records, keepExisting){
8063 this.clearSelections();
8065 var ds = this.grid.ds;
8066 for(var i = 0, len = records.length; i < len; i++){
8067 this.selectRow(ds.indexOf(records[i]), true);
8072 * Gets the number of selected rows.
8075 getCount : function(){
8076 return this.selections.length;
8080 * Selects the first row in the grid.
8082 selectFirstRow : function(){
8087 * Select the last row.
8088 * @param {Boolean} keepExisting (optional) True to keep existing selections
8090 selectLastRow : function(keepExisting){
8091 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8095 * Selects the row immediately following the last selected row.
8096 * @param {Boolean} keepExisting (optional) True to keep existing selections
8098 selectNext : function(keepExisting){
8099 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8100 this.selectRow(this.last+1, keepExisting);
8101 var view = this.grid.view ? this.grid.view : this.grid;
8102 view.focusRow(this.last);
8107 * Selects the row that precedes the last selected row.
8108 * @param {Boolean} keepExisting (optional) True to keep existing selections
8110 selectPrevious : function(keepExisting){
8112 this.selectRow(this.last-1, keepExisting);
8113 var view = this.grid.view ? this.grid.view : this.grid;
8114 view.focusRow(this.last);
8119 * Returns the selected records
8120 * @return {Array} Array of selected records
8122 getSelections : function(){
8123 return [].concat(this.selections.items);
8127 * Returns the first selected record.
8130 getSelected : function(){
8131 return this.selections.itemAt(0);
8136 * Clears all selections.
8138 clearSelections : function(fast){
8143 var ds = this.grid.ds;
8144 var s = this.selections;
8146 this.deselectRow(ds.indexOfId(r.id));
8150 this.selections.clear();
8159 selectAll : function(){
8163 this.selections.clear();
8164 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8165 this.selectRow(i, true);
8170 * Returns True if there is a selection.
8173 hasSelection : function(){
8174 return this.selections.length > 0;
8178 * Returns True if the specified row is selected.
8179 * @param {Number/Record} record The record or index of the record to check
8182 isSelected : function(index){
8183 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8184 return (r && this.selections.key(r.id) ? true : false);
8188 * Returns True if the specified record id is selected.
8189 * @param {String} id The id of record to check
8192 isIdSelected : function(id){
8193 return (this.selections.key(id) ? true : false);
8197 handleMouseDown : function(e, t)
8199 var view = this.grid.view ? this.grid.view : this.grid;
8201 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8204 if(e.shiftKey && this.last !== false){
8205 var last = this.last;
8206 this.selectRange(last, rowIndex, e.ctrlKey);
8207 this.last = last; // reset the last
8208 view.focusRow(rowIndex);
8210 var isSelected = this.isSelected(rowIndex);
8211 if(e.button !== 0 && isSelected){
8212 view.focusRow(rowIndex);
8213 }else if(e.ctrlKey && isSelected){
8214 this.deselectRow(rowIndex);
8215 }else if(!isSelected){
8216 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8217 view.focusRow(rowIndex);
8220 this.fireEvent("afterselectionchange", this);
8223 handleDragableRowClick : function(grid, rowIndex, e)
8225 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8226 this.selectRow(rowIndex, false);
8227 var view = this.grid.view ? this.grid.view : this.grid;
8228 view.focusRow(rowIndex);
8229 this.fireEvent("afterselectionchange", this);
8234 * Selects multiple rows.
8235 * @param {Array} rows Array of the indexes of the row to select
8236 * @param {Boolean} keepExisting (optional) True to keep existing selections
8238 selectRows : function(rows, keepExisting){
8240 this.clearSelections();
8242 for(var i = 0, len = rows.length; i < len; i++){
8243 this.selectRow(rows[i], true);
8248 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8249 * @param {Number} startRow The index of the first row in the range
8250 * @param {Number} endRow The index of the last row in the range
8251 * @param {Boolean} keepExisting (optional) True to retain existing selections
8253 selectRange : function(startRow, endRow, keepExisting){
8258 this.clearSelections();
8260 if(startRow <= endRow){
8261 for(var i = startRow; i <= endRow; i++){
8262 this.selectRow(i, true);
8265 for(var i = startRow; i >= endRow; i--){
8266 this.selectRow(i, true);
8272 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8273 * @param {Number} startRow The index of the first row in the range
8274 * @param {Number} endRow The index of the last row in the range
8276 deselectRange : function(startRow, endRow, preventViewNotify){
8280 for(var i = startRow; i <= endRow; i++){
8281 this.deselectRow(i, preventViewNotify);
8287 * @param {Number} row The index of the row to select
8288 * @param {Boolean} keepExisting (optional) True to keep existing selections
8290 selectRow : function(index, keepExisting, preventViewNotify){
8291 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8294 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8295 if(!keepExisting || this.singleSelect){
8296 this.clearSelections();
8298 var r = this.grid.ds.getAt(index);
8299 this.selections.add(r);
8300 this.last = this.lastActive = index;
8301 if(!preventViewNotify){
8302 var view = this.grid.view ? this.grid.view : this.grid;
8303 view.onRowSelect(index);
8305 this.fireEvent("rowselect", this, index, r);
8306 this.fireEvent("selectionchange", this);
8312 * @param {Number} row The index of the row to deselect
8314 deselectRow : function(index, preventViewNotify){
8318 if(this.last == index){
8321 if(this.lastActive == index){
8322 this.lastActive = false;
8324 var r = this.grid.ds.getAt(index);
8325 this.selections.remove(r);
8326 if(!preventViewNotify){
8327 var view = this.grid.view ? this.grid.view : this.grid;
8328 view.onRowDeselect(index);
8330 this.fireEvent("rowdeselect", this, index);
8331 this.fireEvent("selectionchange", this);
8335 restoreLast : function(){
8337 this.last = this._last;
8342 acceptsNav : function(row, col, cm){
8343 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8347 onEditorKey : function(field, e){
8348 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8353 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8355 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8357 }else if(k == e.ENTER && !e.ctrlKey){
8361 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8363 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8365 }else if(k == e.ESC){
8369 g.startEditing(newCell[0], newCell[1]);
8374 * Ext JS Library 1.1.1
8375 * Copyright(c) 2006-2007, Ext JS, LLC.
8377 * Originally Released Under LGPL - original licence link has changed is not relivant.
8380 * <script type="text/javascript">
8385 * @class Roo.grid.ColumnModel
8386 * @extends Roo.util.Observable
8387 * This is the default implementation of a ColumnModel used by the Grid. It defines
8388 * the columns in the grid.
8391 var colModel = new Roo.grid.ColumnModel([
8392 {header: "Ticker", width: 60, sortable: true, locked: true},
8393 {header: "Company Name", width: 150, sortable: true},
8394 {header: "Market Cap.", width: 100, sortable: true},
8395 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8396 {header: "Employees", width: 100, sortable: true, resizable: false}
8401 * The config options listed for this class are options which may appear in each
8402 * individual column definition.
8403 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8405 * @param {Object} config An Array of column config objects. See this class's
8406 * config objects for details.
8408 Roo.grid.ColumnModel = function(config){
8410 * The config passed into the constructor
8412 this.config = []; //config;
8415 // if no id, create one
8416 // if the column does not have a dataIndex mapping,
8417 // map it to the order it is in the config
8418 for(var i = 0, len = config.length; i < len; i++){
8419 this.addColumn(config[i]);
8424 * The width of columns which have no width specified (defaults to 100)
8427 this.defaultWidth = 100;
8430 * Default sortable of columns which have no sortable specified (defaults to false)
8433 this.defaultSortable = false;
8437 * @event widthchange
8438 * Fires when the width of a column changes.
8439 * @param {ColumnModel} this
8440 * @param {Number} columnIndex The column index
8441 * @param {Number} newWidth The new width
8443 "widthchange": true,
8445 * @event headerchange
8446 * Fires when the text of a header changes.
8447 * @param {ColumnModel} this
8448 * @param {Number} columnIndex The column index
8449 * @param {Number} newText The new header text
8451 "headerchange": true,
8453 * @event hiddenchange
8454 * Fires when a column is hidden or "unhidden".
8455 * @param {ColumnModel} this
8456 * @param {Number} columnIndex The column index
8457 * @param {Boolean} hidden true if hidden, false otherwise
8459 "hiddenchange": true,
8461 * @event columnmoved
8462 * Fires when a column is moved.
8463 * @param {ColumnModel} this
8464 * @param {Number} oldIndex
8465 * @param {Number} newIndex
8467 "columnmoved" : true,
8469 * @event columlockchange
8470 * Fires when a column's locked state is changed
8471 * @param {ColumnModel} this
8472 * @param {Number} colIndex
8473 * @param {Boolean} locked true if locked
8475 "columnlockchange" : true
8477 Roo.grid.ColumnModel.superclass.constructor.call(this);
8479 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8481 * @cfg {String} header [required] The header text to display in the Grid view.
8484 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8487 * @cfg {String} smHeader Header at Bootsrap Small width
8490 * @cfg {String} mdHeader Header at Bootsrap Medium width
8493 * @cfg {String} lgHeader Header at Bootsrap Large width
8496 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8499 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8500 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8501 * specified, the column's index is used as an index into the Record's data Array.
8504 * @cfg {Number} width The initial width in pixels of the column. Using this
8505 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8508 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8509 * Defaults to the value of the {@link #defaultSortable} property.
8510 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8513 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8516 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8519 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8522 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8525 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8526 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8527 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8528 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8531 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8534 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8537 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8540 * @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)
8543 * @cfg {String} tooltip mouse over tooltip text
8546 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8549 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8552 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8555 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8558 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8561 * Returns the id of the column at the specified index.
8562 * @param {Number} index The column index
8563 * @return {String} the id
8565 getColumnId : function(index){
8566 return this.config[index].id;
8570 * Returns the column for a specified id.
8571 * @param {String} id The column id
8572 * @return {Object} the column
8574 getColumnById : function(id){
8575 return this.lookup[id];
8580 * Returns the column Object for a specified dataIndex.
8581 * @param {String} dataIndex The column dataIndex
8582 * @return {Object|Boolean} the column or false if not found
8584 getColumnByDataIndex: function(dataIndex){
8585 var index = this.findColumnIndex(dataIndex);
8586 return index > -1 ? this.config[index] : false;
8590 * Returns the index for a specified column id.
8591 * @param {String} id The column id
8592 * @return {Number} the index, or -1 if not found
8594 getIndexById : function(id){
8595 for(var i = 0, len = this.config.length; i < len; i++){
8596 if(this.config[i].id == id){
8604 * Returns the index for a specified column dataIndex.
8605 * @param {String} dataIndex The column dataIndex
8606 * @return {Number} the index, or -1 if not found
8609 findColumnIndex : function(dataIndex){
8610 for(var i = 0, len = this.config.length; i < len; i++){
8611 if(this.config[i].dataIndex == dataIndex){
8619 moveColumn : function(oldIndex, newIndex){
8620 var c = this.config[oldIndex];
8621 this.config.splice(oldIndex, 1);
8622 this.config.splice(newIndex, 0, c);
8623 this.dataMap = null;
8624 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8627 isLocked : function(colIndex){
8628 return this.config[colIndex].locked === true;
8631 setLocked : function(colIndex, value, suppressEvent){
8632 if(this.isLocked(colIndex) == value){
8635 this.config[colIndex].locked = value;
8637 this.fireEvent("columnlockchange", this, colIndex, value);
8641 getTotalLockedWidth : function(){
8643 for(var i = 0; i < this.config.length; i++){
8644 if(this.isLocked(i) && !this.isHidden(i)){
8645 this.totalWidth += this.getColumnWidth(i);
8651 getLockedCount : function(){
8652 for(var i = 0, len = this.config.length; i < len; i++){
8653 if(!this.isLocked(i)){
8658 return this.config.length;
8662 * Returns the number of columns.
8665 getColumnCount : function(visibleOnly){
8666 if(visibleOnly === true){
8668 for(var i = 0, len = this.config.length; i < len; i++){
8669 if(!this.isHidden(i)){
8675 return this.config.length;
8679 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8680 * @param {Function} fn
8681 * @param {Object} scope (optional)
8682 * @return {Array} result
8684 getColumnsBy : function(fn, scope){
8686 for(var i = 0, len = this.config.length; i < len; i++){
8687 var c = this.config[i];
8688 if(fn.call(scope||this, c, i) === true){
8696 * Returns true if the specified column is sortable.
8697 * @param {Number} col The column index
8700 isSortable : function(col){
8701 if(typeof this.config[col].sortable == "undefined"){
8702 return this.defaultSortable;
8704 return this.config[col].sortable;
8708 * Returns the rendering (formatting) function defined for the column.
8709 * @param {Number} col The column index.
8710 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8712 getRenderer : function(col){
8713 if(!this.config[col].renderer){
8714 return Roo.grid.ColumnModel.defaultRenderer;
8716 return this.config[col].renderer;
8720 * Sets the rendering (formatting) function for a column.
8721 * @param {Number} col The column index
8722 * @param {Function} fn The function to use to process the cell's raw data
8723 * to return HTML markup for the grid view. The render function is called with
8724 * the following parameters:<ul>
8725 * <li>Data value.</li>
8726 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8727 * <li>css A CSS style string to apply to the table cell.</li>
8728 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8729 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8730 * <li>Row index</li>
8731 * <li>Column index</li>
8732 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8734 setRenderer : function(col, fn){
8735 this.config[col].renderer = fn;
8739 * Returns the width for the specified column.
8740 * @param {Number} col The column index
8741 * @param (optional) {String} gridSize bootstrap width size.
8744 getColumnWidth : function(col, gridSize)
8746 var cfg = this.config[col];
8748 if (typeof(gridSize) == 'undefined') {
8749 return cfg.width * 1 || this.defaultWidth;
8751 if (gridSize === false) { // if we set it..
8752 return cfg.width || false;
8754 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8756 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8757 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8760 return cfg[ sizes[i] ];
8767 * Sets the width for a column.
8768 * @param {Number} col The column index
8769 * @param {Number} width The new width
8771 setColumnWidth : function(col, width, suppressEvent){
8772 this.config[col].width = width;
8773 this.totalWidth = null;
8775 this.fireEvent("widthchange", this, col, width);
8780 * Returns the total width of all columns.
8781 * @param {Boolean} includeHidden True to include hidden column widths
8784 getTotalWidth : function(includeHidden){
8785 if(!this.totalWidth){
8786 this.totalWidth = 0;
8787 for(var i = 0, len = this.config.length; i < len; i++){
8788 if(includeHidden || !this.isHidden(i)){
8789 this.totalWidth += this.getColumnWidth(i);
8793 return this.totalWidth;
8797 * Returns the header for the specified column.
8798 * @param {Number} col The column index
8801 getColumnHeader : function(col){
8802 return this.config[col].header;
8806 * Sets the header for a column.
8807 * @param {Number} col The column index
8808 * @param {String} header The new header
8810 setColumnHeader : function(col, header){
8811 this.config[col].header = header;
8812 this.fireEvent("headerchange", this, col, header);
8816 * Returns the tooltip for the specified column.
8817 * @param {Number} col The column index
8820 getColumnTooltip : function(col){
8821 return this.config[col].tooltip;
8824 * Sets the tooltip for a column.
8825 * @param {Number} col The column index
8826 * @param {String} tooltip The new tooltip
8828 setColumnTooltip : function(col, tooltip){
8829 this.config[col].tooltip = tooltip;
8833 * Returns the dataIndex for the specified column.
8834 * @param {Number} col The column index
8837 getDataIndex : function(col){
8838 return this.config[col].dataIndex;
8842 * Sets the dataIndex for a column.
8843 * @param {Number} col The column index
8844 * @param {Number} dataIndex The new dataIndex
8846 setDataIndex : function(col, dataIndex){
8847 this.config[col].dataIndex = dataIndex;
8853 * Returns true if the cell is editable.
8854 * @param {Number} colIndex The column index
8855 * @param {Number} rowIndex The row index - this is nto actually used..?
8858 isCellEditable : function(colIndex, rowIndex){
8859 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8863 * Returns the editor defined for the cell/column.
8864 * return false or null to disable editing.
8865 * @param {Number} colIndex The column index
8866 * @param {Number} rowIndex The row index
8869 getCellEditor : function(colIndex, rowIndex){
8870 return this.config[colIndex].editor;
8874 * Sets if a column is editable.
8875 * @param {Number} col The column index
8876 * @param {Boolean} editable True if the column is editable
8878 setEditable : function(col, editable){
8879 this.config[col].editable = editable;
8884 * Returns true if the column is hidden.
8885 * @param {Number} colIndex The column index
8888 isHidden : function(colIndex){
8889 return this.config[colIndex].hidden;
8894 * Returns true if the column width cannot be changed
8896 isFixed : function(colIndex){
8897 return this.config[colIndex].fixed;
8901 * Returns true if the column can be resized
8904 isResizable : function(colIndex){
8905 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8908 * Sets if a column is hidden.
8909 * @param {Number} colIndex The column index
8910 * @param {Boolean} hidden True if the column is hidden
8912 setHidden : function(colIndex, hidden){
8913 this.config[colIndex].hidden = hidden;
8914 this.totalWidth = null;
8915 this.fireEvent("hiddenchange", this, colIndex, hidden);
8919 * Sets the editor for a column.
8920 * @param {Number} col The column index
8921 * @param {Object} editor The editor object
8923 setEditor : function(col, editor){
8924 this.config[col].editor = editor;
8927 * Add a column (experimental...) - defaults to adding to the end..
8928 * @param {Object} config
8930 addColumn : function(c)
8933 var i = this.config.length;
8936 if(typeof c.dataIndex == "undefined"){
8939 if(typeof c.renderer == "string"){
8940 c.renderer = Roo.util.Format[c.renderer];
8942 if(typeof c.id == "undefined"){
8945 if(c.editor && c.editor.xtype){
8946 c.editor = Roo.factory(c.editor, Roo.grid);
8948 if(c.editor && c.editor.isFormField){
8949 c.editor = new Roo.grid.GridEditor(c.editor);
8951 this.lookup[c.id] = c;
8956 Roo.grid.ColumnModel.defaultRenderer = function(value)
8958 if(typeof value == "object") {
8961 if(typeof value == "string" && value.length < 1){
8965 return String.format("{0}", value);
8968 // Alias for backwards compatibility
8969 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8972 * Ext JS Library 1.1.1
8973 * Copyright(c) 2006-2007, Ext JS, LLC.
8975 * Originally Released Under LGPL - original licence link has changed is not relivant.
8978 * <script type="text/javascript">
8982 * @class Roo.LoadMask
8983 * A simple utility class for generically masking elements while loading data. If the element being masked has
8984 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8985 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8986 * element's UpdateManager load indicator and will be destroyed after the initial load.
8988 * Create a new LoadMask
8989 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8990 * @param {Object} config The config object
8992 Roo.LoadMask = function(el, config){
8993 this.el = Roo.get(el);
8994 Roo.apply(this, config);
8996 this.store.on('beforeload', this.onBeforeLoad, this);
8997 this.store.on('load', this.onLoad, this);
8998 this.store.on('loadexception', this.onLoadException, this);
8999 this.removeMask = false;
9001 var um = this.el.getUpdateManager();
9002 um.showLoadIndicator = false; // disable the default indicator
9003 um.on('beforeupdate', this.onBeforeLoad, this);
9004 um.on('update', this.onLoad, this);
9005 um.on('failure', this.onLoad, this);
9006 this.removeMask = true;
9010 Roo.LoadMask.prototype = {
9012 * @cfg {Boolean} removeMask
9013 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9014 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9019 * The text to display in a centered loading message box (defaults to 'Loading...')
9023 * @cfg {String} msgCls
9024 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9026 msgCls : 'x-mask-loading',
9029 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9035 * Disables the mask to prevent it from being displayed
9037 disable : function(){
9038 this.disabled = true;
9042 * Enables the mask so that it can be displayed
9044 enable : function(){
9045 this.disabled = false;
9048 onLoadException : function()
9052 if (typeof(arguments[3]) != 'undefined') {
9053 Roo.MessageBox.alert("Error loading",arguments[3]);
9057 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9058 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9065 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9070 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9074 onBeforeLoad : function(){
9076 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9081 destroy : function(){
9083 this.store.un('beforeload', this.onBeforeLoad, this);
9084 this.store.un('load', this.onLoad, this);
9085 this.store.un('loadexception', this.onLoadException, this);
9087 var um = this.el.getUpdateManager();
9088 um.un('beforeupdate', this.onBeforeLoad, this);
9089 um.un('update', this.onLoad, this);
9090 um.un('failure', this.onLoad, this);
9094 * @class Roo.bootstrap.Table
9096 * @extends Roo.bootstrap.Component
9097 * @children Roo.bootstrap.TableBody
9098 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9099 * Similar to Roo.grid.Grid
9101 var table = Roo.factory({
9103 xns : Roo.bootstrap,
9104 autoSizeColumns: true,
9111 sortInfo : { direction : 'ASC', field: 'name' },
9113 xtype : 'HttpProxy',
9116 url : 'https://example.com/some.data.url.json'
9119 xtype : 'JsonReader',
9121 fields : [ 'id', 'name', whatever' ],
9128 xtype : 'ColumnModel',
9132 dataIndex : 'is_in_group',
9135 renderer : function(v, x , r) {
9137 return String.format("{0}", v)
9143 xtype : 'RowSelectionModel',
9144 xns : Roo.bootstrap.Table
9145 // you can add listeners to catch selection change here....
9151 grid.render(Roo.get("some-div"));
9154 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9159 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9160 * @cfg {Roo.data.Store} store The data store to use
9161 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9163 * @cfg {String} cls table class
9166 * @cfg {string} empty_results Text to display for no results
9167 * @cfg {boolean} striped Should the rows be alternative striped
9168 * @cfg {boolean} bordered Add borders to the table
9169 * @cfg {boolean} hover Add hover highlighting
9170 * @cfg {boolean} condensed Format condensed
9171 * @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,
9172 * also adds table-responsive (see bootstrap docs for details)
9173 * @cfg {Boolean} loadMask (true|false) default false
9174 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9175 * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9176 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9177 * @cfg {Boolean} rowSelection (true|false) default false
9178 * @cfg {Boolean} cellSelection (true|false) default false
9179 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9180 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9181 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9182 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9183 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9184 * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9187 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9190 * Create a new Table
9191 * @param {Object} config The config object
9194 Roo.bootstrap.Table = function(config)
9196 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9199 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9200 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9201 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9202 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9204 this.view = this; // compat with grid.
9206 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9208 this.sm.grid = this;
9209 this.selModel = Roo.factory(this.sm, Roo.grid);
9210 this.sm = this.selModel;
9211 this.sm.xmodule = this.xmodule || false;
9214 if (this.cm && typeof(this.cm.config) == 'undefined') {
9215 this.colModel = new Roo.grid.ColumnModel(this.cm);
9216 this.cm = this.colModel;
9217 this.cm.xmodule = this.xmodule || false;
9220 this.store= Roo.factory(this.store, Roo.data);
9221 this.ds = this.store;
9222 this.ds.xmodule = this.xmodule || false;
9225 if (this.footer && this.store) {
9226 this.footer.dataSource = this.ds;
9227 this.footer = Roo.factory(this.footer);
9234 * Fires when a cell is clicked
9235 * @param {Roo.bootstrap.Table} this
9236 * @param {Roo.Element} el
9237 * @param {Number} rowIndex
9238 * @param {Number} columnIndex
9239 * @param {Roo.EventObject} e
9243 * @event celldblclick
9244 * Fires when a cell is double clicked
9245 * @param {Roo.bootstrap.Table} this
9246 * @param {Roo.Element} el
9247 * @param {Number} rowIndex
9248 * @param {Number} columnIndex
9249 * @param {Roo.EventObject} e
9251 "celldblclick" : true,
9254 * Fires when a row is clicked
9255 * @param {Roo.bootstrap.Table} this
9256 * @param {Roo.Element} el
9257 * @param {Number} rowIndex
9258 * @param {Roo.EventObject} e
9262 * @event rowdblclick
9263 * Fires when a row is double clicked
9264 * @param {Roo.bootstrap.Table} this
9265 * @param {Roo.Element} el
9266 * @param {Number} rowIndex
9267 * @param {Roo.EventObject} e
9269 "rowdblclick" : true,
9272 * Fires when a mouseover occur
9273 * @param {Roo.bootstrap.Table} this
9274 * @param {Roo.Element} el
9275 * @param {Number} rowIndex
9276 * @param {Number} columnIndex
9277 * @param {Roo.EventObject} e
9282 * Fires when a mouseout occur
9283 * @param {Roo.bootstrap.Table} this
9284 * @param {Roo.Element} el
9285 * @param {Number} rowIndex
9286 * @param {Number} columnIndex
9287 * @param {Roo.EventObject} e
9292 * Fires when a row is rendered, so you can change add a style to it.
9293 * @param {Roo.bootstrap.Table} this
9294 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9298 * @event rowsrendered
9299 * Fires when all the rows have been rendered
9300 * @param {Roo.bootstrap.Table} this
9302 'rowsrendered' : true,
9304 * @event contextmenu
9305 * The raw contextmenu event for the entire grid.
9306 * @param {Roo.EventObject} e
9308 "contextmenu" : true,
9310 * @event rowcontextmenu
9311 * Fires when a row is right clicked
9312 * @param {Roo.bootstrap.Table} this
9313 * @param {Number} rowIndex
9314 * @param {Roo.EventObject} e
9316 "rowcontextmenu" : true,
9318 * @event cellcontextmenu
9319 * Fires when a cell is right clicked
9320 * @param {Roo.bootstrap.Table} this
9321 * @param {Number} rowIndex
9322 * @param {Number} cellIndex
9323 * @param {Roo.EventObject} e
9325 "cellcontextmenu" : true,
9327 * @event headercontextmenu
9328 * Fires when a header is right clicked
9329 * @param {Roo.bootstrap.Table} this
9330 * @param {Number} columnIndex
9331 * @param {Roo.EventObject} e
9333 "headercontextmenu" : true,
9336 * The raw mousedown event for the entire grid.
9337 * @param {Roo.EventObject} e
9344 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9362 enableColumnResize: true,
9363 disableAutoSize: false,
9365 rowSelection : false,
9366 cellSelection : false,
9369 minColumnWidth : 50,
9371 // Roo.Element - the tbody
9372 bodyEl: false, // <tbody> Roo.Element - thead element
9373 headEl: false, // <thead> Roo.Element - thead element
9374 resizeProxy : false, // proxy element for dragging?
9378 container: false, // used by gridpanel...
9384 auto_hide_footer : false,
9386 view: false, // actually points to this..
9388 getAutoCreate : function()
9390 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9397 // this get's auto added by panel.Grid
9398 if (this.scrollBody) {
9399 cfg.cls += ' table-body-fixed';
9402 cfg.cls += ' table-striped';
9406 cfg.cls += ' table-hover';
9408 if (this.bordered) {
9409 cfg.cls += ' table-bordered';
9411 if (this.condensed) {
9412 cfg.cls += ' table-condensed';
9415 if (this.responsive) {
9416 cfg.cls += ' table-responsive';
9420 cfg.cls+= ' ' +this.cls;
9426 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9429 if(this.store || this.cm){
9430 if(this.headerShow){
9431 cfg.cn.push(this.renderHeader());
9434 cfg.cn.push(this.renderBody());
9436 if(this.footerShow || this.footerRow){
9437 cfg.cn.push(this.renderFooter());
9440 // where does this come from?
9441 //cfg.cls+= ' TableGrid';
9444 return { cn : [ cfg ] };
9447 initEvents : function()
9449 if(!this.store || !this.cm){
9452 if (this.selModel) {
9453 this.selModel.initEvents();
9457 //Roo.log('initEvents with ds!!!!');
9459 this.bodyEl = this.el.select('tbody', true).first();
9460 this.headEl = this.el.select('thead', true).first();
9461 this.mainFoot = this.el.select('tfoot', true).first();
9466 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9467 e.on('click', this.sort, this);
9471 // why is this done????? = it breaks dialogs??
9472 //this.parent().el.setStyle('position', 'relative');
9476 this.footer.parentId = this.id;
9477 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9480 this.el.select('tfoot tr td').first().addClass('hide');
9485 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9488 this.store.on('load', this.onLoad, this);
9489 this.store.on('beforeload', this.onBeforeLoad, this);
9490 this.store.on('update', this.onUpdate, this);
9491 this.store.on('add', this.onAdd, this);
9492 this.store.on("clear", this.clear, this);
9494 this.el.on("contextmenu", this.onContextMenu, this);
9497 this.cm.on("headerchange", this.onHeaderChange, this);
9498 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9500 //?? does bodyEl get replaced on render?
9501 this.bodyEl.on("click", this.onClick, this);
9502 this.bodyEl.on("dblclick", this.onDblClick, this);
9503 this.bodyEl.on('scroll', this.onBodyScroll, this);
9505 // guessing mainbody will work - this relays usually caught by selmodel at present.
9506 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9509 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9512 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9513 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9518 // Compatibility with grid - we implement all the view features at present.
9519 getView : function()
9524 initCSS : function()
9526 if(this.disableAutoSize) {
9530 var cm = this.cm, styles = [];
9531 this.CSS.removeStyleSheet(this.id + '-cssrules');
9532 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9533 // we can honour xs/sm/md/xl as widths...
9534 // we first have to decide what widht we are currently at...
9535 var sz = Roo.getGridSize();
9539 var cols = []; // visable cols.
9541 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9542 var w = cm.getColumnWidth(i, false);
9544 cols.push( { rel : false, abs : 0 });
9548 cols.push( { rel : false, abs : w });
9550 last = i; // not really..
9553 var w = cm.getColumnWidth(i, sz);
9558 cols.push( { rel : w, abs : false });
9561 var avail = this.bodyEl.dom.clientWidth - total_abs;
9563 var unitWidth = Math.floor(avail / total);
9564 var rem = avail - (unitWidth * total);
9566 var hidden, width, pos = 0 , splithide , left;
9567 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9569 hidden = 'display:none;';
9571 width = 'width:0px;';
9573 if(!cm.isHidden(i)){
9577 // we can honour xs/sm/md/xl ?
9578 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9580 hidden = 'display:none;';
9582 // width should return a small number...
9584 w+=rem; // add the remaining with..
9587 left = "left:" + (pos -4) + "px;";
9588 width = "width:" + w+ "px;";
9591 if (this.responsive) {
9594 hidden = cm.isHidden(i) ? 'display:none;' : '';
9595 splithide = 'display: none;';
9598 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9601 splithide = 'display:none;';
9604 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9605 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9606 // this is the popover version..
9607 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9612 //Roo.log(styles.join(''));
9613 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9619 onContextMenu : function(e, t)
9621 this.processEvent("contextmenu", e);
9624 processEvent : function(name, e)
9626 if (name != 'touchstart' ) {
9627 this.fireEvent(name, e);
9630 var t = e.getTarget();
9632 var cell = Roo.get(t);
9638 if(cell.findParent('tfoot', false, true)){
9642 if(cell.findParent('thead', false, true)){
9644 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9645 cell = Roo.get(t).findParent('th', false, true);
9647 Roo.log("failed to find th in thead?");
9648 Roo.log(e.getTarget());
9653 var cellIndex = cell.dom.cellIndex;
9655 var ename = name == 'touchstart' ? 'click' : name;
9656 this.fireEvent("header" + ename, this, cellIndex, e);
9661 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9662 cell = Roo.get(t).findParent('td', false, true);
9664 Roo.log("failed to find th in tbody?");
9665 Roo.log(e.getTarget());
9670 var row = cell.findParent('tr', false, true);
9671 var cellIndex = cell.dom.cellIndex;
9672 var rowIndex = row.dom.rowIndex - 1;
9676 this.fireEvent("row" + name, this, rowIndex, e);
9680 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9686 onMouseover : function(e, el)
9688 var cell = Roo.get(el);
9694 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9695 cell = cell.findParent('td', false, true);
9698 var row = cell.findParent('tr', false, true);
9699 var cellIndex = cell.dom.cellIndex;
9700 var rowIndex = row.dom.rowIndex - 1; // start from 0
9702 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9706 onMouseout : function(e, el)
9708 var cell = Roo.get(el);
9714 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9715 cell = cell.findParent('td', false, true);
9718 var row = cell.findParent('tr', false, true);
9719 var cellIndex = cell.dom.cellIndex;
9720 var rowIndex = row.dom.rowIndex - 1; // start from 0
9722 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9726 onClick : function(e, el)
9728 var cell = Roo.get(el);
9730 if(!cell || (!this.cellSelection && !this.rowSelection)){
9734 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9735 cell = cell.findParent('td', false, true);
9738 if(!cell || typeof(cell) == 'undefined'){
9742 var row = cell.findParent('tr', false, true);
9744 if(!row || typeof(row) == 'undefined'){
9748 var cellIndex = cell.dom.cellIndex;
9749 var rowIndex = this.getRowIndex(row);
9751 // why??? - should these not be based on SelectionModel?
9752 //if(this.cellSelection){
9753 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9756 //if(this.rowSelection){
9757 this.fireEvent('rowclick', this, row, rowIndex, e);
9762 onDblClick : function(e,el)
9764 var cell = Roo.get(el);
9766 if(!cell || (!this.cellSelection && !this.rowSelection)){
9770 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9771 cell = cell.findParent('td', false, true);
9774 if(!cell || typeof(cell) == 'undefined'){
9778 var row = cell.findParent('tr', false, true);
9780 if(!row || typeof(row) == 'undefined'){
9784 var cellIndex = cell.dom.cellIndex;
9785 var rowIndex = this.getRowIndex(row);
9787 if(this.cellSelection){
9788 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9791 if(this.rowSelection){
9792 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9795 findRowIndex : function(el)
9797 var cell = Roo.get(el);
9801 var row = cell.findParent('tr', false, true);
9803 if(!row || typeof(row) == 'undefined'){
9806 return this.getRowIndex(row);
9808 sort : function(e,el)
9810 var col = Roo.get(el);
9812 if(!col.hasClass('sortable')){
9816 var sort = col.attr('sort');
9819 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9823 this.store.sortInfo = {field : sort, direction : dir};
9826 Roo.log("calling footer first");
9827 this.footer.onClick('first');
9830 this.store.load({ params : { start : 0 } });
9834 renderHeader : function()
9842 this.totalWidth = 0;
9844 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9846 var config = cm.config[i];
9850 cls : 'x-hcol-' + i,
9853 html: cm.getColumnHeader(i)
9856 var tooltip = cm.getColumnTooltip(i);
9858 c.tooltip = tooltip;
9864 if(typeof(config.sortable) != 'undefined' && config.sortable){
9865 c.cls += ' sortable';
9866 c.html = '<i class="fa"></i>' + c.html;
9869 // could use BS4 hidden-..-down
9871 if(typeof(config.lgHeader) != 'undefined'){
9872 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9875 if(typeof(config.mdHeader) != 'undefined'){
9876 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9879 if(typeof(config.smHeader) != 'undefined'){
9880 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9883 if(typeof(config.xsHeader) != 'undefined'){
9884 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9891 if(typeof(config.tooltip) != 'undefined'){
9892 c.tooltip = config.tooltip;
9895 if(typeof(config.colspan) != 'undefined'){
9896 c.colspan = config.colspan;
9899 // hidden is handled by CSS now
9901 if(typeof(config.dataIndex) != 'undefined'){
9902 c.sort = config.dataIndex;
9907 if(typeof(config.align) != 'undefined' && config.align.length){
9908 c.style += ' text-align:' + config.align + ';';
9911 /* width is done in CSS
9912 *if(typeof(config.width) != 'undefined'){
9913 c.style += ' width:' + config.width + 'px;';
9914 this.totalWidth += config.width;
9916 this.totalWidth += 100; // assume minimum of 100 per column?
9920 if(typeof(config.cls) != 'undefined'){
9921 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9923 // this is the bit that doesnt reall work at all...
9925 if (this.responsive) {
9928 ['xs','sm','md','lg'].map(function(size){
9930 if(typeof(config[size]) == 'undefined'){
9934 if (!config[size]) { // 0 = hidden
9935 // BS 4 '0' is treated as hide that column and below.
9936 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9940 c.cls += ' col-' + size + '-' + config[size] + (
9941 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9949 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9960 renderBody : function()
9970 colspan : this.cm.getColumnCount()
9980 renderFooter : function()
9990 colspan : this.cm.getColumnCount()
10000 onLoad : function()
10002 // Roo.log('ds onload');
10007 var ds = this.store;
10009 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10010 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10011 if (_this.store.sortInfo) {
10013 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10014 e.select('i', true).addClass(['fa-arrow-up']);
10017 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10018 e.select('i', true).addClass(['fa-arrow-down']);
10023 var tbody = this.bodyEl;
10025 if(ds.getCount() > 0){
10026 ds.data.each(function(d,rowIndex){
10027 var row = this.renderRow(cm, ds, rowIndex);
10029 tbody.createChild(row);
10033 if(row.cellObjects.length){
10034 Roo.each(row.cellObjects, function(r){
10035 _this.renderCellObject(r);
10040 } else if (this.empty_results.length) {
10041 this.el.mask(this.empty_results, 'no-spinner');
10044 var tfoot = this.el.select('tfoot', true).first();
10046 if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10048 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10050 var total = this.ds.getTotalCount();
10052 if(this.footer.pageSize < total){
10053 this.mainFoot.show();
10057 if(!this.footerShow && this.footerRow) {
10064 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10065 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10068 cls : ' x-fcol-' + i,
10076 tfoot.dom.innerHTML = '';
10078 tfoot.createChild(tr);
10081 Roo.each(this.el.select('tbody td', true).elements, function(e){
10082 e.on('mouseover', _this.onMouseover, _this);
10085 Roo.each(this.el.select('tbody td', true).elements, function(e){
10086 e.on('mouseout', _this.onMouseout, _this);
10088 this.fireEvent('rowsrendered', this);
10092 this.initCSS(); /// resize cols
10098 onUpdate : function(ds,record)
10100 this.refreshRow(record);
10104 onRemove : function(ds, record, index, isUpdate){
10105 if(isUpdate !== true){
10106 this.fireEvent("beforerowremoved", this, index, record);
10108 var bt = this.bodyEl.dom;
10110 var rows = this.el.select('tbody > tr', true).elements;
10112 if(typeof(rows[index]) != 'undefined'){
10113 bt.removeChild(rows[index].dom);
10116 // if(bt.rows[index]){
10117 // bt.removeChild(bt.rows[index]);
10120 if(isUpdate !== true){
10121 //this.stripeRows(index);
10122 //this.syncRowHeights(index, index);
10124 this.fireEvent("rowremoved", this, index, record);
10128 onAdd : function(ds, records, rowIndex)
10130 //Roo.log('on Add called');
10131 // - note this does not handle multiple adding very well..
10132 var bt = this.bodyEl.dom;
10133 for (var i =0 ; i < records.length;i++) {
10134 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10135 //Roo.log(records[i]);
10136 //Roo.log(this.store.getAt(rowIndex+i));
10137 this.insertRow(this.store, rowIndex + i, false);
10144 refreshRow : function(record){
10145 var ds = this.store, index;
10146 if(typeof record == 'number'){
10148 record = ds.getAt(index);
10150 index = ds.indexOf(record);
10152 return; // should not happen - but seems to
10155 this.insertRow(ds, index, true);
10157 this.onRemove(ds, record, index+1, true);
10159 //this.syncRowHeights(index, index);
10161 this.fireEvent("rowupdated", this, index, record);
10163 // private - called by RowSelection
10164 onRowSelect : function(rowIndex){
10165 var row = this.getRowDom(rowIndex);
10166 row.addClass(['bg-info','info']);
10168 // private - called by RowSelection
10169 onRowDeselect : function(rowIndex)
10171 if (rowIndex < 0) {
10174 var row = this.getRowDom(rowIndex);
10175 row.removeClass(['bg-info','info']);
10178 * Focuses the specified row.
10179 * @param {Number} row The row index
10181 focusRow : function(row)
10183 //Roo.log('GridView.focusRow');
10184 var x = this.bodyEl.dom.scrollLeft;
10185 this.focusCell(row, 0, false);
10186 this.bodyEl.dom.scrollLeft = x;
10190 * Focuses the specified cell.
10191 * @param {Number} row The row index
10192 * @param {Number} col The column index
10193 * @param {Boolean} hscroll false to disable horizontal scrolling
10195 focusCell : function(row, col, hscroll)
10197 //Roo.log('GridView.focusCell');
10198 var el = this.ensureVisible(row, col, hscroll);
10199 // not sure what focusEL achives = it's a <a> pos relative
10200 //this.focusEl.alignTo(el, "tl-tl");
10202 // this.focusEl.focus();
10204 // this.focusEl.focus.defer(1, this.focusEl);
10209 * Scrolls the specified cell into view
10210 * @param {Number} row The row index
10211 * @param {Number} col The column index
10212 * @param {Boolean} hscroll false to disable horizontal scrolling
10214 ensureVisible : function(row, col, hscroll)
10216 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10217 //return null; //disable for testing.
10218 if(typeof row != "number"){
10219 row = row.rowIndex;
10221 if(row < 0 && row >= this.ds.getCount()){
10224 col = (col !== undefined ? col : 0);
10226 while(cm.isHidden(col)){
10230 var el = this.getCellDom(row, col);
10234 var c = this.bodyEl.dom;
10236 var ctop = parseInt(el.offsetTop, 10);
10237 var cleft = parseInt(el.offsetLeft, 10);
10238 var cbot = ctop + el.offsetHeight;
10239 var cright = cleft + el.offsetWidth;
10241 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10242 var ch = 0; //?? header is not withing the area?
10243 var stop = parseInt(c.scrollTop, 10);
10244 var sleft = parseInt(c.scrollLeft, 10);
10245 var sbot = stop + ch;
10246 var sright = sleft + c.clientWidth;
10248 Roo.log('GridView.ensureVisible:' +
10250 ' c.clientHeight:' + c.clientHeight +
10251 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10259 c.scrollTop = ctop;
10260 //Roo.log("set scrolltop to ctop DISABLE?");
10261 }else if(cbot > sbot){
10262 //Roo.log("set scrolltop to cbot-ch");
10263 c.scrollTop = cbot-ch;
10266 if(hscroll !== false){
10268 c.scrollLeft = cleft;
10269 }else if(cright > sright){
10270 c.scrollLeft = cright-c.clientWidth;
10278 insertRow : function(dm, rowIndex, isUpdate){
10281 this.fireEvent("beforerowsinserted", this, rowIndex);
10283 //var s = this.getScrollState();
10284 var row = this.renderRow(this.cm, this.store, rowIndex);
10285 // insert before rowIndex..
10286 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10290 if(row.cellObjects.length){
10291 Roo.each(row.cellObjects, function(r){
10292 _this.renderCellObject(r);
10297 this.fireEvent("rowsinserted", this, rowIndex);
10298 //this.syncRowHeights(firstRow, lastRow);
10299 //this.stripeRows(firstRow);
10306 getRowDom : function(rowIndex)
10308 var rows = this.el.select('tbody > tr', true).elements;
10310 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10313 getCellDom : function(rowIndex, colIndex)
10315 var row = this.getRowDom(rowIndex);
10316 if (row === false) {
10319 var cols = row.select('td', true).elements;
10320 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10324 // returns the object tree for a tr..
10327 renderRow : function(cm, ds, rowIndex)
10329 var d = ds.getAt(rowIndex);
10333 cls : 'x-row-' + rowIndex,
10337 var cellObjects = [];
10339 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10340 var config = cm.config[i];
10342 var renderer = cm.getRenderer(i);
10346 if(typeof(renderer) !== 'undefined'){
10347 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10349 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10350 // and are rendered into the cells after the row is rendered - using the id for the element.
10352 if(typeof(value) === 'object'){
10362 rowIndex : rowIndex,
10367 this.fireEvent('rowclass', this, rowcfg);
10371 // this might end up displaying HTML?
10372 // this is too messy... - better to only do it on columsn you know are going to be too long
10373 //tooltip : (typeof(value) === 'object') ? '' : value,
10374 cls : rowcfg.rowClass + ' x-col-' + i,
10376 html: (typeof(value) === 'object') ? '' : value
10383 if(typeof(config.colspan) != 'undefined'){
10384 td.colspan = config.colspan;
10389 if(typeof(config.align) != 'undefined' && config.align.length){
10390 td.style += ' text-align:' + config.align + ';';
10392 if(typeof(config.valign) != 'undefined' && config.valign.length){
10393 td.style += ' vertical-align:' + config.valign + ';';
10396 if(typeof(config.width) != 'undefined'){
10397 td.style += ' width:' + config.width + 'px;';
10401 if(typeof(config.cursor) != 'undefined'){
10402 td.style += ' cursor:' + config.cursor + ';';
10405 if(typeof(config.cls) != 'undefined'){
10406 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10408 if (this.responsive) {
10409 ['xs','sm','md','lg'].map(function(size){
10411 if(typeof(config[size]) == 'undefined'){
10417 if (!config[size]) { // 0 = hidden
10418 // BS 4 '0' is treated as hide that column and below.
10419 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10423 td.cls += ' col-' + size + '-' + config[size] + (
10424 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10434 row.cellObjects = cellObjects;
10442 onBeforeLoad : function()
10444 this.el.unmask(); // if needed.
10451 this.el.select('tbody', true).first().dom.innerHTML = '';
10454 * Show or hide a row.
10455 * @param {Number} rowIndex to show or hide
10456 * @param {Boolean} state hide
10458 setRowVisibility : function(rowIndex, state)
10460 var bt = this.bodyEl.dom;
10462 var rows = this.el.select('tbody > tr', true).elements;
10464 if(typeof(rows[rowIndex]) == 'undefined'){
10467 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10472 getSelectionModel : function(){
10473 if(!this.selModel){
10474 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10476 return this.selModel;
10479 * Render the Roo.bootstrap object from renderder
10481 renderCellObject : function(r)
10485 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10487 var t = r.cfg.render(r.container);
10490 Roo.each(r.cfg.cn, function(c){
10492 container: t.getChildContainer(),
10495 _this.renderCellObject(child);
10500 * get the Row Index from a dom element.
10501 * @param {Roo.Element} row The row to look for
10502 * @returns {Number} the row
10504 getRowIndex : function(row)
10508 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10519 * get the header TH element for columnIndex
10520 * @param {Number} columnIndex
10521 * @returns {Roo.Element}
10523 getHeaderIndex: function(colIndex)
10525 var cols = this.headEl.select('th', true).elements;
10526 return cols[colIndex];
10529 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10530 * @param {domElement} cell to look for
10531 * @returns {Number} the column
10533 getCellIndex : function(cell)
10535 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10537 return parseInt(id[1], 10);
10542 * Returns the grid's underlying element = used by panel.Grid
10543 * @return {Element} The element
10545 getGridEl : function(){
10549 * Forces a resize - used by panel.Grid
10550 * @return {Element} The element
10552 autoSize : function()
10554 if(this.disableAutoSize) {
10557 //var ctr = Roo.get(this.container.dom.parentElement);
10558 var ctr = Roo.get(this.el.dom);
10560 var thd = this.getGridEl().select('thead',true).first();
10561 var tbd = this.getGridEl().select('tbody', true).first();
10562 var tfd = this.getGridEl().select('tfoot', true).first();
10564 var cw = ctr.getWidth();
10565 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10569 tbd.setWidth(ctr.getWidth());
10570 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10571 // this needs fixing for various usage - currently only hydra job advers I think..
10573 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10575 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10578 cw = Math.max(cw, this.totalWidth);
10579 this.getGridEl().select('tbody tr',true).setWidth(cw);
10582 // resize 'expandable coloumn?
10584 return; // we doe not have a view in this design..
10587 onBodyScroll: function()
10589 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10591 this.headEl.setStyle({
10592 'position' : 'relative',
10593 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10599 var scrollHeight = this.bodyEl.dom.scrollHeight;
10601 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10603 var height = this.bodyEl.getHeight();
10605 if(scrollHeight - height == scrollTop) {
10607 var total = this.ds.getTotalCount();
10609 if(this.footer.cursor + this.footer.pageSize < total){
10611 this.footer.ds.load({
10613 start : this.footer.cursor + this.footer.pageSize,
10614 limit : this.footer.pageSize
10623 onColumnSplitterMoved : function(i, diff)
10625 this.userResized = true;
10627 var cm = this.colModel;
10629 var w = this.getHeaderIndex(i).getWidth() + diff;
10632 cm.setColumnWidth(i, w, true);
10634 //var cid = cm.getColumnId(i); << not used in this version?
10635 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10637 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10638 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10639 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10641 //this.updateSplitters();
10642 //this.layout(); << ??
10643 this.fireEvent("columnresize", i, w);
10645 onHeaderChange : function()
10647 var header = this.renderHeader();
10648 var table = this.el.select('table', true).first();
10650 this.headEl.remove();
10651 this.headEl = table.createChild(header, this.bodyEl, false);
10653 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10654 e.on('click', this.sort, this);
10657 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10658 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10663 onHiddenChange : function(colModel, colIndex, hidden)
10666 this.cm.setHidden()
10667 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10668 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10670 this.CSS.updateRule(thSelector, "display", "");
10671 this.CSS.updateRule(tdSelector, "display", "");
10674 this.CSS.updateRule(thSelector, "display", "none");
10675 this.CSS.updateRule(tdSelector, "display", "none");
10678 // onload calls initCSS()
10679 this.onHeaderChange();
10683 setColumnWidth: function(col_index, width)
10685 // width = "md-2 xs-2..."
10686 if(!this.colModel.config[col_index]) {
10690 var w = width.split(" ");
10692 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10694 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10697 for(var j = 0; j < w.length; j++) {
10703 var size_cls = w[j].split("-");
10705 if(!Number.isInteger(size_cls[1] * 1)) {
10709 if(!this.colModel.config[col_index][size_cls[0]]) {
10713 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10717 h_row[0].classList.replace(
10718 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10719 "col-"+size_cls[0]+"-"+size_cls[1]
10722 for(var i = 0; i < rows.length; i++) {
10724 var size_cls = w[j].split("-");
10726 if(!Number.isInteger(size_cls[1] * 1)) {
10730 if(!this.colModel.config[col_index][size_cls[0]]) {
10734 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10738 rows[i].classList.replace(
10739 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10740 "col-"+size_cls[0]+"-"+size_cls[1]
10744 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10749 // currently only used to find the split on drag..
10750 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10755 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10756 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10765 * @class Roo.bootstrap.TableCell
10766 * @extends Roo.bootstrap.Component
10767 * @children Roo.bootstrap.Component
10768 * @parent Roo.bootstrap.TableRow
10769 * Bootstrap TableCell class
10771 * @cfg {String} html cell contain text
10772 * @cfg {String} cls cell class
10773 * @cfg {String} tag cell tag (td|th) default td
10774 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10775 * @cfg {String} align Aligns the content in a cell
10776 * @cfg {String} axis Categorizes cells
10777 * @cfg {String} bgcolor Specifies the background color of a cell
10778 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10779 * @cfg {Number} colspan Specifies the number of columns a cell should span
10780 * @cfg {String} headers Specifies one or more header cells a cell is related to
10781 * @cfg {Number} height Sets the height of a cell
10782 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10783 * @cfg {Number} rowspan Sets the number of rows a cell should span
10784 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10785 * @cfg {String} valign Vertical aligns the content in a cell
10786 * @cfg {Number} width Specifies the width of a cell
10789 * Create a new TableCell
10790 * @param {Object} config The config object
10793 Roo.bootstrap.TableCell = function(config){
10794 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10797 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10817 getAutoCreate : function(){
10818 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10825 cfg.tag = this.tag;
10838 cfg.align=this.align
10843 if (this.bgcolor) {
10844 cfg.bgcolor=this.bgcolor
10846 if (this.charoff) {
10847 cfg.charoff=this.charoff
10849 if (this.colspan) {
10850 cfg.colspan=this.colspan
10852 if (this.headers) {
10853 cfg.headers=this.headers
10856 cfg.height=this.height
10859 cfg.nowrap=this.nowrap
10861 if (this.rowspan) {
10862 cfg.rowspan=this.rowspan
10865 cfg.scope=this.scope
10868 cfg.valign=this.valign
10871 cfg.width=this.width
10890 * @class Roo.bootstrap.TableRow
10891 * @extends Roo.bootstrap.Component
10892 * @children Roo.bootstrap.TableCell
10893 * @parent Roo.bootstrap.TableBody
10894 * Bootstrap TableRow class
10895 * @cfg {String} cls row class
10896 * @cfg {String} align Aligns the content in a table row
10897 * @cfg {String} bgcolor Specifies a background color for a table row
10898 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10899 * @cfg {String} valign Vertical aligns the content in a table row
10902 * Create a new TableRow
10903 * @param {Object} config The config object
10906 Roo.bootstrap.TableRow = function(config){
10907 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10910 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10918 getAutoCreate : function(){
10919 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10926 cfg.cls = this.cls;
10929 cfg.align = this.align;
10932 cfg.bgcolor = this.bgcolor;
10935 cfg.charoff = this.charoff;
10938 cfg.valign = this.valign;
10956 * @class Roo.bootstrap.TableBody
10957 * @extends Roo.bootstrap.Component
10958 * @children Roo.bootstrap.TableRow
10959 * @parent Roo.bootstrap.Table
10960 * Bootstrap TableBody class
10961 * @cfg {String} cls element class
10962 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10963 * @cfg {String} align Aligns the content inside the element
10964 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10965 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10968 * Create a new TableBody
10969 * @param {Object} config The config object
10972 Roo.bootstrap.TableBody = function(config){
10973 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10976 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10984 getAutoCreate : function(){
10985 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10995 cfg.tag = this.tag;
10999 cfg.align = this.align;
11002 cfg.charoff = this.charoff;
11005 cfg.valign = this.valign;
11012 // initEvents : function()
11015 // if(!this.store){
11019 // this.store = Roo.factory(this.store, Roo.data);
11020 // this.store.on('load', this.onLoad, this);
11022 // this.store.load();
11026 // onLoad: function ()
11028 // this.fireEvent('load', this);
11038 * Ext JS Library 1.1.1
11039 * Copyright(c) 2006-2007, Ext JS, LLC.
11041 * Originally Released Under LGPL - original licence link has changed is not relivant.
11044 * <script type="text/javascript">
11047 // as we use this in bootstrap.
11048 Roo.namespace('Roo.form');
11050 * @class Roo.form.Action
11051 * Internal Class used to handle form actions
11053 * @param {Roo.form.BasicForm} el The form element or its id
11054 * @param {Object} config Configuration options
11059 // define the action interface
11060 Roo.form.Action = function(form, options){
11062 this.options = options || {};
11065 * Client Validation Failed
11068 Roo.form.Action.CLIENT_INVALID = 'client';
11070 * Server Validation Failed
11073 Roo.form.Action.SERVER_INVALID = 'server';
11075 * Connect to Server Failed
11078 Roo.form.Action.CONNECT_FAILURE = 'connect';
11080 * Reading Data from Server Failed
11083 Roo.form.Action.LOAD_FAILURE = 'load';
11085 Roo.form.Action.prototype = {
11087 failureType : undefined,
11088 response : undefined,
11089 result : undefined,
11091 // interface method
11092 run : function(options){
11096 // interface method
11097 success : function(response){
11101 // interface method
11102 handleResponse : function(response){
11106 // default connection failure
11107 failure : function(response){
11109 this.response = response;
11110 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11111 this.form.afterAction(this, false);
11114 processResponse : function(response){
11115 this.response = response;
11116 if(!response.responseText){
11119 this.result = this.handleResponse(response);
11120 return this.result;
11123 // utility functions used internally
11124 getUrl : function(appendParams){
11125 var url = this.options.url || this.form.url || this.form.el.dom.action;
11127 var p = this.getParams();
11129 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11135 getMethod : function(){
11136 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11139 getParams : function(){
11140 var bp = this.form.baseParams;
11141 var p = this.options.params;
11143 if(typeof p == "object"){
11144 p = Roo.urlEncode(Roo.applyIf(p, bp));
11145 }else if(typeof p == 'string' && bp){
11146 p += '&' + Roo.urlEncode(bp);
11149 p = Roo.urlEncode(bp);
11154 createCallback : function(){
11156 success: this.success,
11157 failure: this.failure,
11159 timeout: (this.form.timeout*1000),
11160 upload: this.form.fileUpload ? this.success : undefined
11165 Roo.form.Action.Submit = function(form, options){
11166 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11169 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11172 haveProgress : false,
11173 uploadComplete : false,
11175 // uploadProgress indicator.
11176 uploadProgress : function()
11178 if (!this.form.progressUrl) {
11182 if (!this.haveProgress) {
11183 Roo.MessageBox.progress("Uploading", "Uploading");
11185 if (this.uploadComplete) {
11186 Roo.MessageBox.hide();
11190 this.haveProgress = true;
11192 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11194 var c = new Roo.data.Connection();
11196 url : this.form.progressUrl,
11201 success : function(req){
11202 //console.log(data);
11206 rdata = Roo.decode(req.responseText)
11208 Roo.log("Invalid data from server..");
11212 if (!rdata || !rdata.success) {
11214 Roo.MessageBox.alert(Roo.encode(rdata));
11217 var data = rdata.data;
11219 if (this.uploadComplete) {
11220 Roo.MessageBox.hide();
11225 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11226 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11229 this.uploadProgress.defer(2000,this);
11232 failure: function(data) {
11233 Roo.log('progress url failed ');
11244 // run get Values on the form, so it syncs any secondary forms.
11245 this.form.getValues();
11247 var o = this.options;
11248 var method = this.getMethod();
11249 var isPost = method == 'POST';
11250 if(o.clientValidation === false || this.form.isValid()){
11252 if (this.form.progressUrl) {
11253 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11254 (new Date() * 1) + '' + Math.random());
11259 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11260 form:this.form.el.dom,
11261 url:this.getUrl(!isPost),
11263 params:isPost ? this.getParams() : null,
11264 isUpload: this.form.fileUpload,
11265 formData : this.form.formData
11268 this.uploadProgress();
11270 }else if (o.clientValidation !== false){ // client validation failed
11271 this.failureType = Roo.form.Action.CLIENT_INVALID;
11272 this.form.afterAction(this, false);
11276 success : function(response)
11278 this.uploadComplete= true;
11279 if (this.haveProgress) {
11280 Roo.MessageBox.hide();
11284 var result = this.processResponse(response);
11285 if(result === true || result.success){
11286 this.form.afterAction(this, true);
11290 this.form.markInvalid(result.errors);
11291 this.failureType = Roo.form.Action.SERVER_INVALID;
11293 this.form.afterAction(this, false);
11295 failure : function(response)
11297 this.uploadComplete= true;
11298 if (this.haveProgress) {
11299 Roo.MessageBox.hide();
11302 this.response = response;
11303 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11304 this.form.afterAction(this, false);
11307 handleResponse : function(response){
11308 if(this.form.errorReader){
11309 var rs = this.form.errorReader.read(response);
11312 for(var i = 0, len = rs.records.length; i < len; i++) {
11313 var r = rs.records[i];
11314 errors[i] = r.data;
11317 if(errors.length < 1){
11321 success : rs.success,
11327 var rt = response.responseText;
11328 if (rt.match(/^\<!--\[CDATA\[/)) {
11329 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11330 rt = rt.replace(/\]\]--\>$/,'');
11333 ret = Roo.decode(rt);
11337 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11347 Roo.form.Action.Load = function(form, options){
11348 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11349 this.reader = this.form.reader;
11352 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11357 Roo.Ajax.request(Roo.apply(
11358 this.createCallback(), {
11359 method:this.getMethod(),
11360 url:this.getUrl(false),
11361 params:this.getParams()
11365 success : function(response){
11367 var result = this.processResponse(response);
11368 if(result === true || !result.success || !result.data){
11369 this.failureType = Roo.form.Action.LOAD_FAILURE;
11370 this.form.afterAction(this, false);
11373 this.form.clearInvalid();
11374 this.form.setValues(result.data);
11375 this.form.afterAction(this, true);
11378 handleResponse : function(response){
11379 if(this.form.reader){
11380 var rs = this.form.reader.read(response);
11381 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11383 success : rs.success,
11387 return Roo.decode(response.responseText);
11391 Roo.form.Action.ACTION_TYPES = {
11392 'load' : Roo.form.Action.Load,
11393 'submit' : Roo.form.Action.Submit
11402 * @class Roo.bootstrap.form.Form
11403 * @extends Roo.bootstrap.Component
11404 * @children Roo.bootstrap.Component
11405 * Bootstrap Form class
11406 * @cfg {String} method GET | POST (default POST)
11407 * @cfg {String} labelAlign top | left (default top)
11408 * @cfg {String} align left | right - for navbars
11409 * @cfg {Boolean} loadMask load mask when submit (default true)
11413 * Create a new Form
11414 * @param {Object} config The config object
11418 Roo.bootstrap.form.Form = function(config){
11420 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11422 Roo.bootstrap.form.Form.popover.apply();
11426 * @event clientvalidation
11427 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11428 * @param {Form} this
11429 * @param {Boolean} valid true if the form has passed client-side validation
11431 clientvalidation: true,
11433 * @event beforeaction
11434 * Fires before any action is performed. Return false to cancel the action.
11435 * @param {Form} this
11436 * @param {Action} action The action to be performed
11438 beforeaction: true,
11440 * @event actionfailed
11441 * Fires when an action fails.
11442 * @param {Form} this
11443 * @param {Action} action The action that failed
11445 actionfailed : true,
11447 * @event actioncomplete
11448 * Fires when an action is completed.
11449 * @param {Form} this
11450 * @param {Action} action The action that completed
11452 actioncomplete : true
11456 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11459 * @cfg {String} method
11460 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11464 * @cfg {String} url
11465 * The URL to use for form actions if one isn't supplied in the action options.
11468 * @cfg {Boolean} fileUpload
11469 * Set to true if this form is a file upload.
11473 * @cfg {Object} baseParams
11474 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11478 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11482 * @cfg {Sting} align (left|right) for navbar forms
11487 activeAction : null,
11490 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11491 * element by passing it or its id or mask the form itself by passing in true.
11494 waitMsgTarget : false,
11499 * @cfg {Boolean} errorMask (true|false) default false
11504 * @cfg {Number} maskOffset Default 100
11509 * @cfg {Boolean} maskBody
11513 getAutoCreate : function(){
11517 method : this.method || 'POST',
11518 id : this.id || Roo.id(),
11521 if (this.parent().xtype.match(/^Nav/)) {
11522 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11526 if (this.labelAlign == 'left' ) {
11527 cfg.cls += ' form-horizontal';
11533 initEvents : function()
11535 this.el.on('submit', this.onSubmit, this);
11536 // this was added as random key presses on the form where triggering form submit.
11537 this.el.on('keypress', function(e) {
11538 if (e.getCharCode() != 13) {
11541 // we might need to allow it for textareas.. and some other items.
11542 // check e.getTarget().
11544 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11548 Roo.log("keypress blocked");
11550 e.preventDefault();
11556 onSubmit : function(e){
11561 * Returns true if client-side validation on the form is successful.
11564 isValid : function(){
11565 var items = this.getItems();
11567 var target = false;
11569 items.each(function(f){
11575 Roo.log('invalid field: ' + f.name);
11579 if(!target && f.el.isVisible(true)){
11585 if(this.errorMask && !valid){
11586 Roo.bootstrap.form.Form.popover.mask(this, target);
11593 * Returns true if any fields in this form have changed since their original load.
11596 isDirty : function(){
11598 var items = this.getItems();
11599 items.each(function(f){
11609 * Performs a predefined action (submit or load) or custom actions you define on this form.
11610 * @param {String} actionName The name of the action type
11611 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11612 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11613 * accept other config options):
11615 Property Type Description
11616 ---------------- --------------- ----------------------------------------------------------------------------------
11617 url String The url for the action (defaults to the form's url)
11618 method String The form method to use (defaults to the form's method, or POST if not defined)
11619 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11620 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11621 validate the form on the client (defaults to false)
11623 * @return {BasicForm} this
11625 doAction : function(action, options){
11626 if(typeof action == 'string'){
11627 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11629 if(this.fireEvent('beforeaction', this, action) !== false){
11630 this.beforeAction(action);
11631 action.run.defer(100, action);
11637 beforeAction : function(action){
11638 var o = action.options;
11643 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11645 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11648 // not really supported yet.. ??
11650 //if(this.waitMsgTarget === true){
11651 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11652 //}else if(this.waitMsgTarget){
11653 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11654 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11656 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11662 afterAction : function(action, success){
11663 this.activeAction = null;
11664 var o = action.options;
11669 Roo.get(document.body).unmask();
11675 //if(this.waitMsgTarget === true){
11676 // this.el.unmask();
11677 //}else if(this.waitMsgTarget){
11678 // this.waitMsgTarget.unmask();
11680 // Roo.MessageBox.updateProgress(1);
11681 // Roo.MessageBox.hide();
11688 Roo.callback(o.success, o.scope, [this, action]);
11689 this.fireEvent('actioncomplete', this, action);
11693 // failure condition..
11694 // we have a scenario where updates need confirming.
11695 // eg. if a locking scenario exists..
11696 // we look for { errors : { needs_confirm : true }} in the response.
11698 (typeof(action.result) != 'undefined') &&
11699 (typeof(action.result.errors) != 'undefined') &&
11700 (typeof(action.result.errors.needs_confirm) != 'undefined')
11703 Roo.log("not supported yet");
11706 Roo.MessageBox.confirm(
11707 "Change requires confirmation",
11708 action.result.errorMsg,
11713 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11723 Roo.callback(o.failure, o.scope, [this, action]);
11724 // show an error message if no failed handler is set..
11725 if (!this.hasListener('actionfailed')) {
11726 Roo.log("need to add dialog support");
11728 Roo.MessageBox.alert("Error",
11729 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11730 action.result.errorMsg :
11731 "Saving Failed, please check your entries or try again"
11736 this.fireEvent('actionfailed', this, action);
11741 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11742 * @param {String} id The value to search for
11745 findField : function(id){
11746 var items = this.getItems();
11747 var field = items.get(id);
11749 items.each(function(f){
11750 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11757 return field || null;
11760 * Mark fields in this form invalid in bulk.
11761 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11762 * @return {BasicForm} this
11764 markInvalid : function(errors){
11765 if(errors instanceof Array){
11766 for(var i = 0, len = errors.length; i < len; i++){
11767 var fieldError = errors[i];
11768 var f = this.findField(fieldError.id);
11770 f.markInvalid(fieldError.msg);
11776 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11777 field.markInvalid(errors[id]);
11781 //Roo.each(this.childForms || [], function (f) {
11782 // f.markInvalid(errors);
11789 * Set values for fields in this form in bulk.
11790 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11791 * @return {BasicForm} this
11793 setValues : function(values){
11794 if(values instanceof Array){ // array of objects
11795 for(var i = 0, len = values.length; i < len; i++){
11797 var f = this.findField(v.id);
11799 f.setValue(v.value);
11800 if(this.trackResetOnLoad){
11801 f.originalValue = f.getValue();
11805 }else{ // object hash
11808 if(typeof values[id] != 'function' && (field = this.findField(id))){
11810 if (field.setFromData &&
11811 field.valueField &&
11812 field.displayField &&
11813 // combos' with local stores can
11814 // be queried via setValue()
11815 // to set their value..
11816 (field.store && !field.store.isLocal)
11820 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11821 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11822 field.setFromData(sd);
11824 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11826 field.setFromData(values);
11829 field.setValue(values[id]);
11833 if(this.trackResetOnLoad){
11834 field.originalValue = field.getValue();
11840 //Roo.each(this.childForms || [], function (f) {
11841 // f.setValues(values);
11848 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11849 * they are returned as an array.
11850 * @param {Boolean} asString
11853 getValues : function(asString){
11854 //if (this.childForms) {
11855 // copy values from the child forms
11856 // Roo.each(this.childForms, function (f) {
11857 // this.setValues(f.getValues());
11863 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11864 if(asString === true){
11867 return Roo.urlDecode(fs);
11871 * Returns the fields in this form as an object with key/value pairs.
11872 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11875 getFieldValues : function(with_hidden)
11877 var items = this.getItems();
11879 items.each(function(f){
11881 if (!f.getName()) {
11885 var v = f.getValue();
11887 if (f.inputType =='radio') {
11888 if (typeof(ret[f.getName()]) == 'undefined') {
11889 ret[f.getName()] = ''; // empty..
11892 if (!f.el.dom.checked) {
11896 v = f.el.dom.value;
11900 if(f.xtype == 'MoneyField'){
11901 ret[f.currencyName] = f.getCurrency();
11904 // not sure if this supported any more..
11905 if ((typeof(v) == 'object') && f.getRawValue) {
11906 v = f.getRawValue() ; // dates..
11908 // combo boxes where name != hiddenName...
11909 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11910 ret[f.name] = f.getRawValue();
11912 ret[f.getName()] = v;
11919 * Clears all invalid messages in this form.
11920 * @return {BasicForm} this
11922 clearInvalid : function(){
11923 var items = this.getItems();
11925 items.each(function(f){
11933 * Resets this form.
11934 * @return {BasicForm} this
11936 reset : function(){
11937 var items = this.getItems();
11938 items.each(function(f){
11942 Roo.each(this.childForms || [], function (f) {
11950 getItems : function()
11952 var r=new Roo.util.MixedCollection(false, function(o){
11953 return o.id || (o.id = Roo.id());
11955 var iter = function(el) {
11962 Roo.each(el.items,function(e) {
11971 hideFields : function(items)
11973 Roo.each(items, function(i){
11975 var f = this.findField(i);
11986 showFields : function(items)
11988 Roo.each(items, function(i){
11990 var f = this.findField(i);
12003 Roo.apply(Roo.bootstrap.form.Form, {
12019 intervalID : false,
12025 if(this.isApplied){
12030 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12031 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12032 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12033 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12036 this.maskEl.top.enableDisplayMode("block");
12037 this.maskEl.left.enableDisplayMode("block");
12038 this.maskEl.bottom.enableDisplayMode("block");
12039 this.maskEl.right.enableDisplayMode("block");
12041 this.toolTip = new Roo.bootstrap.Tooltip({
12042 cls : 'roo-form-error-popover',
12044 'left' : ['r-l', [-2,0], 'right'],
12045 'right' : ['l-r', [2,0], 'left'],
12046 'bottom' : ['tl-bl', [0,2], 'top'],
12047 'top' : [ 'bl-tl', [0,-2], 'bottom']
12051 this.toolTip.render(Roo.get(document.body));
12053 this.toolTip.el.enableDisplayMode("block");
12055 Roo.get(document.body).on('click', function(){
12059 Roo.get(document.body).on('touchstart', function(){
12063 this.isApplied = true
12066 mask : function(form, target)
12070 this.target = target;
12072 if(!this.form.errorMask || !target.el){
12076 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12078 Roo.log(scrollable);
12080 var ot = this.target.el.calcOffsetsTo(scrollable);
12082 var scrollTo = ot[1] - this.form.maskOffset;
12084 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12086 scrollable.scrollTo('top', scrollTo);
12088 var box = this.target.el.getBox();
12090 var zIndex = Roo.bootstrap.Modal.zIndex++;
12093 this.maskEl.top.setStyle('position', 'absolute');
12094 this.maskEl.top.setStyle('z-index', zIndex);
12095 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12096 this.maskEl.top.setLeft(0);
12097 this.maskEl.top.setTop(0);
12098 this.maskEl.top.show();
12100 this.maskEl.left.setStyle('position', 'absolute');
12101 this.maskEl.left.setStyle('z-index', zIndex);
12102 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12103 this.maskEl.left.setLeft(0);
12104 this.maskEl.left.setTop(box.y - this.padding);
12105 this.maskEl.left.show();
12107 this.maskEl.bottom.setStyle('position', 'absolute');
12108 this.maskEl.bottom.setStyle('z-index', zIndex);
12109 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12110 this.maskEl.bottom.setLeft(0);
12111 this.maskEl.bottom.setTop(box.bottom + this.padding);
12112 this.maskEl.bottom.show();
12114 this.maskEl.right.setStyle('position', 'absolute');
12115 this.maskEl.right.setStyle('z-index', zIndex);
12116 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12117 this.maskEl.right.setLeft(box.right + this.padding);
12118 this.maskEl.right.setTop(box.y - this.padding);
12119 this.maskEl.right.show();
12121 this.toolTip.bindEl = this.target.el;
12123 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12125 var tip = this.target.blankText;
12127 if(this.target.getValue() !== '' ) {
12129 if (this.target.invalidText.length) {
12130 tip = this.target.invalidText;
12131 } else if (this.target.regexText.length){
12132 tip = this.target.regexText;
12136 this.toolTip.show(tip);
12138 this.intervalID = window.setInterval(function() {
12139 Roo.bootstrap.form.Form.popover.unmask();
12142 window.onwheel = function(){ return false;};
12144 (function(){ this.isMasked = true; }).defer(500, this);
12148 unmask : function()
12150 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12154 this.maskEl.top.setStyle('position', 'absolute');
12155 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12156 this.maskEl.top.hide();
12158 this.maskEl.left.setStyle('position', 'absolute');
12159 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12160 this.maskEl.left.hide();
12162 this.maskEl.bottom.setStyle('position', 'absolute');
12163 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12164 this.maskEl.bottom.hide();
12166 this.maskEl.right.setStyle('position', 'absolute');
12167 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12168 this.maskEl.right.hide();
12170 this.toolTip.hide();
12172 this.toolTip.el.hide();
12174 window.onwheel = function(){ return true;};
12176 if(this.intervalID){
12177 window.clearInterval(this.intervalID);
12178 this.intervalID = false;
12181 this.isMasked = false;
12191 * Ext JS Library 1.1.1
12192 * Copyright(c) 2006-2007, Ext JS, LLC.
12194 * Originally Released Under LGPL - original licence link has changed is not relivant.
12197 * <script type="text/javascript">
12200 * @class Roo.form.VTypes
12201 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12204 Roo.form.VTypes = function(){
12205 // closure these in so they are only created once.
12206 var alpha = /^[a-zA-Z_]+$/;
12207 var alphanum = /^[a-zA-Z0-9_]+$/;
12208 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12209 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12211 // All these messages and functions are configurable
12214 * The function used to validate email addresses
12215 * @param {String} value The email address
12217 email : function(v){
12218 return email.test(v);
12221 * The error text to display when the email validation function returns false
12224 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12226 * The keystroke filter mask to be applied on email input
12229 emailMask : /[a-z0-9_\.\-@]/i,
12232 * The function used to validate URLs
12233 * @param {String} value The URL
12236 return url.test(v);
12239 * The error text to display when the url validation function returns false
12242 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12245 * The function used to validate alpha values
12246 * @param {String} value The value
12248 alpha : function(v){
12249 return alpha.test(v);
12252 * The error text to display when the alpha validation function returns false
12255 alphaText : 'This field should only contain letters and _',
12257 * The keystroke filter mask to be applied on alpha input
12260 alphaMask : /[a-z_]/i,
12263 * The function used to validate alphanumeric values
12264 * @param {String} value The value
12266 alphanum : function(v){
12267 return alphanum.test(v);
12270 * The error text to display when the alphanumeric validation function returns false
12273 alphanumText : 'This field should only contain letters, numbers and _',
12275 * The keystroke filter mask to be applied on alphanumeric input
12278 alphanumMask : /[a-z0-9_]/i
12288 * @class Roo.bootstrap.form.Input
12289 * @extends Roo.bootstrap.Component
12290 * Bootstrap Input class
12291 * @cfg {Boolean} disabled is it disabled
12292 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12293 * @cfg {String} name name of the input
12294 * @cfg {string} fieldLabel - the label associated
12295 * @cfg {string} placeholder - placeholder to put in text.
12296 * @cfg {string} before - input group add on before
12297 * @cfg {string} after - input group add on after
12298 * @cfg {string} size - (lg|sm) or leave empty..
12299 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12300 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12301 * @cfg {Number} md colspan out of 12 for computer-sized screens
12302 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12303 * @cfg {string} value default value of the input
12304 * @cfg {Number} labelWidth set the width of label
12305 * @cfg {Number} labellg set the width of label (1-12)
12306 * @cfg {Number} labelmd set the width of label (1-12)
12307 * @cfg {Number} labelsm set the width of label (1-12)
12308 * @cfg {Number} labelxs set the width of label (1-12)
12309 * @cfg {String} labelAlign (top|left)
12310 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12311 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12312 * @cfg {String} indicatorpos (left|right) default left
12313 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12314 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12315 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12316 * @cfg {Roo.bootstrap.Button} before Button to show before
12317 * @cfg {Roo.bootstrap.Button} afterButton to show before
12318 * @cfg {String} align (left|center|right) Default left
12319 * @cfg {Boolean} forceFeedback (true|false) Default false
12322 * Create a new Input
12323 * @param {Object} config The config object
12326 Roo.bootstrap.form.Input = function(config){
12328 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12333 * Fires when this field receives input focus.
12334 * @param {Roo.form.Field} this
12339 * Fires when this field loses input focus.
12340 * @param {Roo.form.Field} this
12344 * @event specialkey
12345 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12346 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12347 * @param {Roo.form.Field} this
12348 * @param {Roo.EventObject} e The event object
12353 * Fires just before the field blurs if the field value has changed.
12354 * @param {Roo.form.Field} this
12355 * @param {Mixed} newValue The new value
12356 * @param {Mixed} oldValue The original value
12361 * Fires after the field has been marked as invalid.
12362 * @param {Roo.form.Field} this
12363 * @param {String} msg The validation message
12368 * Fires after the field has been validated with no errors.
12369 * @param {Roo.form.Field} this
12374 * Fires after the key up
12375 * @param {Roo.form.Field} this
12376 * @param {Roo.EventObject} e The event Object
12381 * Fires after the user pastes into input
12382 * @param {Roo.form.Field} this
12383 * @param {Roo.EventObject} e The event Object
12389 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12391 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12392 automatic validation (defaults to "keyup").
12394 validationEvent : "keyup",
12396 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12398 validateOnBlur : true,
12400 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12402 validationDelay : 250,
12404 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12406 focusClass : "x-form-focus", // not needed???
12410 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12412 invalidClass : "has-warning",
12415 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12417 validClass : "has-success",
12420 * @cfg {Boolean} hasFeedback (true|false) default true
12422 hasFeedback : true,
12425 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12427 invalidFeedbackClass : "glyphicon-warning-sign",
12430 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12432 validFeedbackClass : "glyphicon-ok",
12435 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12437 selectOnFocus : false,
12440 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12444 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12449 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12451 disableKeyFilter : false,
12454 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12458 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12462 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12464 blankText : "Please complete this mandatory field",
12467 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12471 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12473 maxLength : Number.MAX_VALUE,
12475 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12477 minLengthText : "The minimum length for this field is {0}",
12479 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12481 maxLengthText : "The maximum length for this field is {0}",
12485 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12486 * If available, this function will be called only after the basic validators all return true, and will be passed the
12487 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12491 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12492 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12493 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12497 * @cfg {String} regexText -- Depricated - use Invalid Text
12502 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12508 autocomplete: false,
12512 inputType : 'text',
12515 placeholder: false,
12520 preventMark: false,
12521 isFormField : true,
12524 labelAlign : false,
12527 formatedValue : false,
12528 forceFeedback : false,
12530 indicatorpos : 'left',
12540 parentLabelAlign : function()
12543 while (parent.parent()) {
12544 parent = parent.parent();
12545 if (typeof(parent.labelAlign) !='undefined') {
12546 return parent.labelAlign;
12553 getAutoCreate : function()
12560 if(this.inputType != 'hidden'){
12561 cfg.cls = 'form-group' //input-group
12567 type : this.inputType,
12568 value : this.value,
12569 cls : 'form-control',
12570 placeholder : this.placeholder || '',
12571 autocomplete : this.autocomplete || 'new-password'
12573 if (this.inputType == 'file') {
12574 input.style = 'overflow:hidden'; // why not in CSS?
12577 if(this.capture.length){
12578 input.capture = this.capture;
12581 if(this.accept.length){
12582 input.accept = this.accept + "/*";
12586 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12589 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12590 input.maxLength = this.maxLength;
12593 if (this.disabled) {
12594 input.disabled=true;
12597 if (this.readOnly) {
12598 input.readonly=true;
12602 input.name = this.name;
12606 input.cls += ' input-' + this.size;
12610 ['xs','sm','md','lg'].map(function(size){
12611 if (settings[size]) {
12612 cfg.cls += ' col-' + size + '-' + settings[size];
12616 var inputblock = input;
12620 cls: 'glyphicon form-control-feedback'
12623 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12626 cls : 'has-feedback',
12634 if (this.before || this.after) {
12637 cls : 'input-group',
12641 if (this.before && typeof(this.before) == 'string') {
12643 inputblock.cn.push({
12645 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12649 if (this.before && typeof(this.before) == 'object') {
12650 this.before = Roo.factory(this.before);
12652 inputblock.cn.push({
12654 cls : 'roo-input-before input-group-prepend input-group-' +
12655 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12659 inputblock.cn.push(input);
12661 if (this.after && typeof(this.after) == 'string') {
12662 inputblock.cn.push({
12664 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12668 if (this.after && typeof(this.after) == 'object') {
12669 this.after = Roo.factory(this.after);
12671 inputblock.cn.push({
12673 cls : 'roo-input-after input-group-append input-group-' +
12674 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12678 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12679 inputblock.cls += ' has-feedback';
12680 inputblock.cn.push(feedback);
12686 cfg = this.getAutoCreateLabel( cfg, inputblock );
12691 if (this.parentType === 'Navbar' && this.parent().bar) {
12692 cfg.cls += ' navbar-form';
12695 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12696 // on BS4 we do this only if not form
12697 cfg.cls += ' navbar-form';
12705 * autocreate the label - also used by textara... ?? and others?
12707 getAutoCreateLabel : function( cfg, inputblock )
12709 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12713 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12714 tooltip : 'This field is required'
12716 if (this.allowBlank ) {
12717 indicator.style = this.allowBlank ? ' display:none' : '';
12719 if (align ==='left' && this.fieldLabel.length) {
12721 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12728 cls : 'control-label col-form-label',
12729 html : this.fieldLabel
12740 var labelCfg = cfg.cn[1];
12741 var contentCfg = cfg.cn[2];
12743 if(this.indicatorpos == 'right'){
12748 cls : 'control-label col-form-label',
12752 html : this.fieldLabel
12766 labelCfg = cfg.cn[0];
12767 contentCfg = cfg.cn[1];
12771 if(this.labelWidth > 12){
12772 labelCfg.style = "width: " + this.labelWidth + 'px';
12775 if(this.labelWidth < 13 && this.labelmd == 0){
12776 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12779 if(this.labellg > 0){
12780 labelCfg.cls += ' col-lg-' + this.labellg;
12781 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12784 if(this.labelmd > 0){
12785 labelCfg.cls += ' col-md-' + this.labelmd;
12786 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12789 if(this.labelsm > 0){
12790 labelCfg.cls += ' col-sm-' + this.labelsm;
12791 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12794 if(this.labelxs > 0){
12795 labelCfg.cls += ' col-xs-' + this.labelxs;
12796 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12800 } else if ( this.fieldLabel.length) {
12807 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12808 tooltip : 'This field is required',
12809 style : this.allowBlank ? ' display:none' : ''
12813 //cls : 'input-group-addon',
12814 html : this.fieldLabel
12822 if(this.indicatorpos == 'right'){
12827 //cls : 'input-group-addon',
12828 html : this.fieldLabel
12833 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12834 tooltip : 'This field is required',
12835 style : this.allowBlank ? ' display:none' : ''
12859 * return the real input element.
12861 inputEl: function ()
12863 return this.el.select('input.form-control',true).first();
12866 tooltipEl : function()
12868 return this.inputEl();
12871 indicatorEl : function()
12873 if (Roo.bootstrap.version == 4) {
12874 return false; // not enabled in v4 yet.
12877 var indicator = this.el.select('i.roo-required-indicator',true).first();
12887 setDisabled : function(v)
12889 var i = this.inputEl().dom;
12891 i.removeAttribute('disabled');
12895 i.setAttribute('disabled','true');
12897 initEvents : function()
12900 this.inputEl().on("keydown" , this.fireKey, this);
12901 this.inputEl().on("focus", this.onFocus, this);
12902 this.inputEl().on("blur", this.onBlur, this);
12904 this.inputEl().relayEvent('keyup', this);
12905 this.inputEl().relayEvent('paste', this);
12907 this.indicator = this.indicatorEl();
12909 if(this.indicator){
12910 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12913 // reference to original value for reset
12914 this.originalValue = this.getValue();
12915 //Roo.form.TextField.superclass.initEvents.call(this);
12916 if(this.validationEvent == 'keyup'){
12917 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12918 this.inputEl().on('keyup', this.filterValidation, this);
12920 else if(this.validationEvent !== false){
12921 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12924 if(this.selectOnFocus){
12925 this.on("focus", this.preFocus, this);
12928 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12929 this.inputEl().on("keypress", this.filterKeys, this);
12931 this.inputEl().relayEvent('keypress', this);
12934 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12935 this.el.on("click", this.autoSize, this);
12938 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12939 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12942 if (typeof(this.before) == 'object') {
12943 this.before.render(this.el.select('.roo-input-before',true).first());
12945 if (typeof(this.after) == 'object') {
12946 this.after.render(this.el.select('.roo-input-after',true).first());
12949 this.inputEl().on('change', this.onChange, this);
12952 filterValidation : function(e){
12953 if(!e.isNavKeyPress()){
12954 this.validationTask.delay(this.validationDelay);
12958 * Validates the field value
12959 * @return {Boolean} True if the value is valid, else false
12961 validate : function(){
12962 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12963 if(this.disabled || this.validateValue(this.getRawValue())){
12968 this.markInvalid();
12974 * Validates a value according to the field's validation rules and marks the field as invalid
12975 * if the validation fails
12976 * @param {Mixed} value The value to validate
12977 * @return {Boolean} True if the value is valid, else false
12979 validateValue : function(value)
12981 if(this.getVisibilityEl().hasClass('hidden')){
12985 if(value.length < 1) { // if it's blank
12986 if(this.allowBlank){
12992 if(value.length < this.minLength){
12995 if(value.length > this.maxLength){
12999 var vt = Roo.form.VTypes;
13000 if(!vt[this.vtype](value, this)){
13004 if(typeof this.validator == "function"){
13005 var msg = this.validator(value);
13006 if (typeof(msg) == 'string') {
13007 this.invalidText = msg;
13014 if(this.regex && !this.regex.test(value)){
13022 fireKey : function(e){
13023 //Roo.log('field ' + e.getKey());
13024 if(e.isNavKeyPress()){
13025 this.fireEvent("specialkey", this, e);
13028 focus : function (selectText){
13030 this.inputEl().focus();
13031 if(selectText === true){
13032 this.inputEl().dom.select();
13038 onFocus : function(){
13039 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13040 // this.el.addClass(this.focusClass);
13042 if(!this.hasFocus){
13043 this.hasFocus = true;
13044 this.startValue = this.getValue();
13045 this.fireEvent("focus", this);
13049 beforeBlur : Roo.emptyFn,
13053 onBlur : function(){
13055 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13056 //this.el.removeClass(this.focusClass);
13058 this.hasFocus = false;
13059 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13062 var v = this.getValue();
13063 if(String(v) !== String(this.startValue)){
13064 this.fireEvent('change', this, v, this.startValue);
13066 this.fireEvent("blur", this);
13069 onChange : function(e)
13071 var v = this.getValue();
13072 if(String(v) !== String(this.startValue)){
13073 this.fireEvent('change', this, v, this.startValue);
13079 * Resets the current field value to the originally loaded value and clears any validation messages
13081 reset : function(){
13082 this.setValue(this.originalValue);
13086 * Returns the name of the field
13087 * @return {Mixed} name The name field
13089 getName: function(){
13093 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13094 * @return {Mixed} value The field value
13096 getValue : function(){
13097 var v = this.inputEl().getValue();
13101 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13102 * @return {Mixed} value The field value
13104 getRawValue : function(){
13105 var v = this.inputEl().getValue();
13111 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13112 * @param {Mixed} value The value to set
13114 setRawValue : function(v){
13115 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13118 selectText : function(start, end){
13119 var v = this.getRawValue();
13121 start = start === undefined ? 0 : start;
13122 end = end === undefined ? v.length : end;
13123 var d = this.inputEl().dom;
13124 if(d.setSelectionRange){
13125 d.setSelectionRange(start, end);
13126 }else if(d.createTextRange){
13127 var range = d.createTextRange();
13128 range.moveStart("character", start);
13129 range.moveEnd("character", v.length-end);
13136 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13137 * @param {Mixed} value The value to set
13139 setValue : function(v){
13142 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13148 processValue : function(value){
13149 if(this.stripCharsRe){
13150 var newValue = value.replace(this.stripCharsRe, '');
13151 if(newValue !== value){
13152 this.setRawValue(newValue);
13159 preFocus : function(){
13161 if(this.selectOnFocus){
13162 this.inputEl().dom.select();
13165 filterKeys : function(e){
13166 var k = e.getKey();
13167 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13170 var c = e.getCharCode(), cc = String.fromCharCode(c);
13171 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13174 if(!this.maskRe.test(cc)){
13179 * Clear any invalid styles/messages for this field
13181 clearInvalid : function(){
13183 if(!this.el || this.preventMark){ // not rendered
13188 this.el.removeClass([this.invalidClass, 'is-invalid']);
13190 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13192 var feedback = this.el.select('.form-control-feedback', true).first();
13195 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13200 if(this.indicator){
13201 this.indicator.removeClass('visible');
13202 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13205 this.fireEvent('valid', this);
13209 * Mark this field as valid
13211 markValid : function()
13213 if(!this.el || this.preventMark){ // not rendered...
13217 this.el.removeClass([this.invalidClass, this.validClass]);
13218 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13220 var feedback = this.el.select('.form-control-feedback', true).first();
13223 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13226 if(this.indicator){
13227 this.indicator.removeClass('visible');
13228 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13236 if(this.allowBlank && !this.getRawValue().length){
13239 if (Roo.bootstrap.version == 3) {
13240 this.el.addClass(this.validClass);
13242 this.inputEl().addClass('is-valid');
13245 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13247 var feedback = this.el.select('.form-control-feedback', true).first();
13250 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13251 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13256 this.fireEvent('valid', this);
13260 * Mark this field as invalid
13261 * @param {String} msg The validation message
13263 markInvalid : function(msg)
13265 if(!this.el || this.preventMark){ // not rendered
13269 this.el.removeClass([this.invalidClass, this.validClass]);
13270 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13272 var feedback = this.el.select('.form-control-feedback', true).first();
13275 this.el.select('.form-control-feedback', true).first().removeClass(
13276 [this.invalidFeedbackClass, this.validFeedbackClass]);
13283 if(this.allowBlank && !this.getRawValue().length){
13287 if(this.indicator){
13288 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13289 this.indicator.addClass('visible');
13291 if (Roo.bootstrap.version == 3) {
13292 this.el.addClass(this.invalidClass);
13294 this.inputEl().addClass('is-invalid');
13299 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13301 var feedback = this.el.select('.form-control-feedback', true).first();
13304 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13306 if(this.getValue().length || this.forceFeedback){
13307 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13314 this.fireEvent('invalid', this, msg);
13317 SafariOnKeyDown : function(event)
13319 // this is a workaround for a password hang bug on chrome/ webkit.
13320 if (this.inputEl().dom.type != 'password') {
13324 var isSelectAll = false;
13326 if(this.inputEl().dom.selectionEnd > 0){
13327 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13329 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13330 event.preventDefault();
13335 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13337 event.preventDefault();
13338 // this is very hacky as keydown always get's upper case.
13340 var cc = String.fromCharCode(event.getCharCode());
13341 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13345 adjustWidth : function(tag, w){
13346 tag = tag.toLowerCase();
13347 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13348 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13349 if(tag == 'input'){
13352 if(tag == 'textarea'){
13355 }else if(Roo.isOpera){
13356 if(tag == 'input'){
13359 if(tag == 'textarea'){
13367 setFieldLabel : function(v)
13369 if(!this.rendered){
13373 if(this.indicatorEl()){
13374 var ar = this.el.select('label > span',true);
13376 if (ar.elements.length) {
13377 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13378 this.fieldLabel = v;
13382 var br = this.el.select('label',true);
13384 if(br.elements.length) {
13385 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13386 this.fieldLabel = v;
13390 Roo.log('Cannot Found any of label > span || label in input');
13394 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13395 this.fieldLabel = v;
13410 * @class Roo.bootstrap.form.TextArea
13411 * @extends Roo.bootstrap.form.Input
13412 * Bootstrap TextArea class
13413 * @cfg {Number} cols Specifies the visible width of a text area
13414 * @cfg {Number} rows Specifies the visible number of lines in a text area
13415 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13416 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13417 * @cfg {string} html text
13420 * Create a new TextArea
13421 * @param {Object} config The config object
13424 Roo.bootstrap.form.TextArea = function(config){
13425 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13429 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13439 getAutoCreate : function(){
13441 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13447 if(this.inputType != 'hidden'){
13448 cfg.cls = 'form-group' //input-group
13456 value : this.value || '',
13457 html: this.html || '',
13458 cls : 'form-control',
13459 placeholder : this.placeholder || ''
13463 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13464 input.maxLength = this.maxLength;
13468 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13472 input.cols = this.cols;
13475 if (this.readOnly) {
13476 input.readonly = true;
13480 input.name = this.name;
13484 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13488 ['xs','sm','md','lg'].map(function(size){
13489 if (settings[size]) {
13490 cfg.cls += ' col-' + size + '-' + settings[size];
13494 var inputblock = input;
13496 if(this.hasFeedback && !this.allowBlank){
13500 cls: 'glyphicon form-control-feedback'
13504 cls : 'has-feedback',
13513 if (this.before || this.after) {
13516 cls : 'input-group',
13520 inputblock.cn.push({
13522 cls : 'input-group-addon',
13527 inputblock.cn.push(input);
13529 if(this.hasFeedback && !this.allowBlank){
13530 inputblock.cls += ' has-feedback';
13531 inputblock.cn.push(feedback);
13535 inputblock.cn.push({
13537 cls : 'input-group-addon',
13545 cfg = this.getAutoCreateLabel( cfg, inputblock );
13549 if (this.disabled) {
13550 input.disabled=true;
13557 * return the real textarea element.
13559 inputEl: function ()
13561 return this.el.select('textarea.form-control',true).first();
13565 * Clear any invalid styles/messages for this field
13567 clearInvalid : function()
13570 if(!this.el || this.preventMark){ // not rendered
13574 var label = this.el.select('label', true).first();
13575 //var icon = this.el.select('i.fa-star', true).first();
13577 //if(label && icon){
13580 this.el.removeClass( this.validClass);
13581 this.inputEl().removeClass('is-invalid');
13583 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13585 var feedback = this.el.select('.form-control-feedback', true).first();
13588 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13593 this.fireEvent('valid', this);
13597 * Mark this field as valid
13599 markValid : function()
13601 if(!this.el || this.preventMark){ // not rendered
13605 this.el.removeClass([this.invalidClass, this.validClass]);
13606 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13608 var feedback = this.el.select('.form-control-feedback', true).first();
13611 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13614 if(this.disabled || this.allowBlank){
13618 var label = this.el.select('label', true).first();
13619 var icon = this.el.select('i.fa-star', true).first();
13621 //if(label && icon){
13624 if (Roo.bootstrap.version == 3) {
13625 this.el.addClass(this.validClass);
13627 this.inputEl().addClass('is-valid');
13631 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13633 var feedback = this.el.select('.form-control-feedback', true).first();
13636 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13637 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13642 this.fireEvent('valid', this);
13646 * Mark this field as invalid
13647 * @param {String} msg The validation message
13649 markInvalid : function(msg)
13651 if(!this.el || this.preventMark){ // not rendered
13655 this.el.removeClass([this.invalidClass, this.validClass]);
13656 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13658 var feedback = this.el.select('.form-control-feedback', true).first();
13661 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13668 var label = this.el.select('label', true).first();
13669 //var icon = this.el.select('i.fa-star', true).first();
13671 //if(!this.getValue().length && label && !icon){
13672 /* this.el.createChild({
13674 cls : 'text-danger fa fa-lg fa-star',
13675 tooltip : 'This field is required',
13676 style : 'margin-right:5px;'
13681 if (Roo.bootstrap.version == 3) {
13682 this.el.addClass(this.invalidClass);
13684 this.inputEl().addClass('is-invalid');
13687 // fixme ... this may be depricated need to test..
13688 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13690 var feedback = this.el.select('.form-control-feedback', true).first();
13693 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13695 if(this.getValue().length || this.forceFeedback){
13696 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13703 this.fireEvent('invalid', this, msg);
13711 * trigger field - base class for combo..
13716 * @class Roo.bootstrap.form.TriggerField
13717 * @extends Roo.bootstrap.form.Input
13718 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13719 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13720 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13721 * for which you can provide a custom implementation. For example:
13723 var trigger = new Roo.bootstrap.form.TriggerField();
13724 trigger.onTriggerClick = myTriggerFn;
13725 trigger.applyTo('my-field');
13728 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13729 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13730 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13731 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13732 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13735 * Create a new TriggerField.
13736 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13737 * to the base TextField)
13739 Roo.bootstrap.form.TriggerField = function(config){
13740 this.mimicing = false;
13741 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13744 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13746 * @cfg {String} triggerClass A CSS class to apply to the trigger
13749 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13754 * @cfg {Boolean} removable (true|false) special filter default false
13758 /** @cfg {Boolean} grow @hide */
13759 /** @cfg {Number} growMin @hide */
13760 /** @cfg {Number} growMax @hide */
13766 autoSize: Roo.emptyFn,
13770 deferHeight : true,
13773 actionMode : 'wrap',
13778 getAutoCreate : function(){
13780 var align = this.labelAlign || this.parentLabelAlign();
13785 cls: 'form-group' //input-group
13792 type : this.inputType,
13793 cls : 'form-control',
13794 autocomplete: 'new-password',
13795 placeholder : this.placeholder || ''
13799 input.name = this.name;
13802 input.cls += ' input-' + this.size;
13805 if (this.disabled) {
13806 input.disabled=true;
13809 var inputblock = input;
13811 if(this.hasFeedback && !this.allowBlank){
13815 cls: 'glyphicon form-control-feedback'
13818 if(this.removable && !this.editable ){
13820 cls : 'has-feedback',
13826 cls : 'roo-combo-removable-btn close'
13833 cls : 'has-feedback',
13842 if(this.removable && !this.editable ){
13844 cls : 'roo-removable',
13850 cls : 'roo-combo-removable-btn close'
13857 if (this.before || this.after) {
13860 cls : 'input-group',
13864 inputblock.cn.push({
13866 cls : 'input-group-addon input-group-prepend input-group-text',
13871 inputblock.cn.push(input);
13873 if(this.hasFeedback && !this.allowBlank){
13874 inputblock.cls += ' has-feedback';
13875 inputblock.cn.push(feedback);
13879 inputblock.cn.push({
13881 cls : 'input-group-addon input-group-append input-group-text',
13890 var ibwrap = inputblock;
13895 cls: 'roo-select2-choices',
13899 cls: 'roo-select2-search-field',
13911 cls: 'roo-select2-container input-group',
13916 cls: 'form-hidden-field'
13922 if(!this.multiple && this.showToggleBtn){
13928 if (this.caret != false) {
13931 cls: 'fa fa-' + this.caret
13938 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13940 Roo.bootstrap.version == 3 ? caret : '',
13943 cls: 'combobox-clear',
13957 combobox.cls += ' roo-select2-container-multi';
13961 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13962 tooltip : 'This field is required'
13965 if (this.allowBlank) {
13968 style : 'display:none'
13974 if (align ==='left' && this.fieldLabel.length) {
13976 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13983 cls : 'control-label',
13984 html : this.fieldLabel
13996 var labelCfg = cfg.cn[1];
13997 var contentCfg = cfg.cn[2];
13999 if(this.indicatorpos == 'right'){
14004 cls : 'control-label',
14008 html : this.fieldLabel
14022 labelCfg = cfg.cn[0];
14023 contentCfg = cfg.cn[1];
14026 if(this.labelWidth > 12){
14027 labelCfg.style = "width: " + this.labelWidth + 'px';
14030 if(this.labelWidth < 13 && this.labelmd == 0){
14031 this.labelmd = this.labelWidth;
14034 if(this.labellg > 0){
14035 labelCfg.cls += ' col-lg-' + this.labellg;
14036 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14039 if(this.labelmd > 0){
14040 labelCfg.cls += ' col-md-' + this.labelmd;
14041 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14044 if(this.labelsm > 0){
14045 labelCfg.cls += ' col-sm-' + this.labelsm;
14046 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14049 if(this.labelxs > 0){
14050 labelCfg.cls += ' col-xs-' + this.labelxs;
14051 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14054 } else if ( this.fieldLabel.length) {
14055 // Roo.log(" label");
14060 //cls : 'input-group-addon',
14061 html : this.fieldLabel
14069 if(this.indicatorpos == 'right'){
14077 html : this.fieldLabel
14091 // Roo.log(" no label && no align");
14098 ['xs','sm','md','lg'].map(function(size){
14099 if (settings[size]) {
14100 cfg.cls += ' col-' + size + '-' + settings[size];
14111 onResize : function(w, h){
14112 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14113 // if(typeof w == 'number'){
14114 // var x = w - this.trigger.getWidth();
14115 // this.inputEl().setWidth(this.adjustWidth('input', x));
14116 // this.trigger.setStyle('left', x+'px');
14121 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14124 getResizeEl : function(){
14125 return this.inputEl();
14129 getPositionEl : function(){
14130 return this.inputEl();
14134 alignErrorIcon : function(){
14135 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14139 initEvents : function(){
14143 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14144 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14145 if(!this.multiple && this.showToggleBtn){
14146 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14147 if(this.hideTrigger){
14148 this.trigger.setDisplayed(false);
14150 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14154 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14157 if(this.removable && !this.editable && !this.tickable){
14158 var close = this.closeTriggerEl();
14161 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14162 close.on('click', this.removeBtnClick, this, close);
14166 //this.trigger.addClassOnOver('x-form-trigger-over');
14167 //this.trigger.addClassOnClick('x-form-trigger-click');
14170 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14174 closeTriggerEl : function()
14176 var close = this.el.select('.roo-combo-removable-btn', true).first();
14177 return close ? close : false;
14180 removeBtnClick : function(e, h, el)
14182 e.preventDefault();
14184 if(this.fireEvent("remove", this) !== false){
14186 this.fireEvent("afterremove", this)
14190 createList : function()
14192 this.list = Roo.get(document.body).createChild({
14193 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14194 cls: 'typeahead typeahead-long dropdown-menu shadow',
14195 style: 'display:none'
14198 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14203 initTrigger : function(){
14208 onDestroy : function(){
14210 this.trigger.removeAllListeners();
14211 // this.trigger.remove();
14214 // this.wrap.remove();
14216 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14220 onFocus : function(){
14221 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14223 if(!this.mimicing){
14224 this.wrap.addClass('x-trigger-wrap-focus');
14225 this.mimicing = true;
14226 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14227 if(this.monitorTab){
14228 this.el.on("keydown", this.checkTab, this);
14235 checkTab : function(e){
14236 if(e.getKey() == e.TAB){
14237 this.triggerBlur();
14242 onBlur : function(){
14247 mimicBlur : function(e, t){
14249 if(!this.wrap.contains(t) && this.validateBlur()){
14250 this.triggerBlur();
14256 triggerBlur : function(){
14257 this.mimicing = false;
14258 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14259 if(this.monitorTab){
14260 this.el.un("keydown", this.checkTab, this);
14262 //this.wrap.removeClass('x-trigger-wrap-focus');
14263 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14267 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14268 validateBlur : function(e, t){
14273 onDisable : function(){
14274 this.inputEl().dom.disabled = true;
14275 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14277 // this.wrap.addClass('x-item-disabled');
14282 onEnable : function(){
14283 this.inputEl().dom.disabled = false;
14284 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14286 // this.el.removeClass('x-item-disabled');
14291 onShow : function(){
14292 var ae = this.getActionEl();
14295 ae.dom.style.display = '';
14296 ae.dom.style.visibility = 'visible';
14302 onHide : function(){
14303 var ae = this.getActionEl();
14304 ae.dom.style.display = 'none';
14308 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14309 * by an implementing function.
14311 * @param {EventObject} e
14313 onTriggerClick : Roo.emptyFn
14321 * @class Roo.bootstrap.form.CardUploader
14322 * @extends Roo.bootstrap.Button
14323 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14324 * @cfg {Number} errorTimeout default 3000
14325 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14326 * @cfg {Array} html The button text.
14330 * Create a new CardUploader
14331 * @param {Object} config The config object
14334 Roo.bootstrap.form.CardUploader = function(config){
14338 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14341 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14349 * When a image is clicked on - and needs to display a slideshow or similar..
14350 * @param {Roo.bootstrap.Card} this
14351 * @param {Object} The image information data
14357 * When a the download link is clicked
14358 * @param {Roo.bootstrap.Card} this
14359 * @param {Object} The image information data contains
14366 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14369 errorTimeout : 3000,
14373 fileCollection : false,
14376 getAutoCreate : function()
14380 cls :'form-group' ,
14385 //cls : 'input-group-addon',
14386 html : this.fieldLabel
14394 value : this.value,
14395 cls : 'd-none form-control'
14400 multiple : 'multiple',
14402 cls : 'd-none roo-card-upload-selector'
14406 cls : 'roo-card-uploader-button-container w-100 mb-2'
14409 cls : 'card-columns roo-card-uploader-container'
14419 getChildContainer : function() /// what children are added to.
14421 return this.containerEl;
14424 getButtonContainer : function() /// what children are added to.
14426 return this.el.select(".roo-card-uploader-button-container").first();
14429 initEvents : function()
14432 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14436 xns: Roo.bootstrap,
14439 container_method : 'getButtonContainer' ,
14440 html : this.html, // fix changable?
14443 'click' : function(btn, e) {
14452 this.urlAPI = (window.createObjectURL && window) ||
14453 (window.URL && URL.revokeObjectURL && URL) ||
14454 (window.webkitURL && webkitURL);
14459 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14461 this.selectorEl.on('change', this.onFileSelected, this);
14464 this.images.forEach(function(img) {
14467 this.images = false;
14469 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14475 onClick : function(e)
14477 e.preventDefault();
14479 this.selectorEl.dom.click();
14483 onFileSelected : function(e)
14485 e.preventDefault();
14487 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14491 Roo.each(this.selectorEl.dom.files, function(file){
14492 this.addFile(file);
14501 addFile : function(file)
14504 if(typeof(file) === 'string'){
14505 throw "Add file by name?"; // should not happen
14509 if(!file || !this.urlAPI){
14519 var url = _this.urlAPI.createObjectURL( file);
14522 id : Roo.bootstrap.form.CardUploader.ID--,
14523 is_uploaded : false,
14527 mimetype : file.type,
14535 * addCard - add an Attachment to the uploader
14536 * @param data - the data about the image to upload
14540 title : "Title of file",
14541 is_uploaded : false,
14542 src : "http://.....",
14543 srcfile : { the File upload object },
14544 mimetype : file.type,
14547 .. any other data...
14553 addCard : function (data)
14555 // hidden input element?
14556 // if the file is not an image...
14557 //then we need to use something other that and header_image
14562 xns : Roo.bootstrap,
14563 xtype : 'CardFooter',
14566 xns : Roo.bootstrap,
14572 xns : Roo.bootstrap,
14574 html : String.format("<small>{0}</small>", data.title),
14575 cls : 'col-10 text-left',
14580 click : function() {
14582 t.fireEvent( "download", t, data );
14588 xns : Roo.bootstrap,
14590 style: 'max-height: 28px; ',
14596 click : function() {
14597 t.removeCard(data.id)
14609 var cn = this.addxtype(
14612 xns : Roo.bootstrap,
14615 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14616 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14617 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14622 initEvents : function() {
14623 Roo.bootstrap.Card.prototype.initEvents.call(this);
14625 this.imgEl = this.el.select('.card-img-top').first();
14627 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14628 this.imgEl.set({ 'pointer' : 'cursor' });
14631 this.getCardFooter().addClass('p-1');
14638 // dont' really need ot update items.
14639 // this.items.push(cn);
14640 this.fileCollection.add(cn);
14642 if (!data.srcfile) {
14643 this.updateInput();
14648 var reader = new FileReader();
14649 reader.addEventListener("load", function() {
14650 data.srcdata = reader.result;
14653 reader.readAsDataURL(data.srcfile);
14658 removeCard : function(id)
14661 var card = this.fileCollection.get(id);
14662 card.data.is_deleted = 1;
14663 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14664 //this.fileCollection.remove(card);
14665 //this.items = this.items.filter(function(e) { return e != card });
14666 // dont' really need ot update items.
14667 card.el.dom.parentNode.removeChild(card.el.dom);
14668 this.updateInput();
14674 this.fileCollection.each(function(card) {
14675 if (card.el.dom && card.el.dom.parentNode) {
14676 card.el.dom.parentNode.removeChild(card.el.dom);
14679 this.fileCollection.clear();
14680 this.updateInput();
14683 updateInput : function()
14686 this.fileCollection.each(function(e) {
14690 this.inputEl().dom.value = JSON.stringify(data);
14700 Roo.bootstrap.form.CardUploader.ID = -1;/*
14702 * Ext JS Library 1.1.1
14703 * Copyright(c) 2006-2007, Ext JS, LLC.
14705 * Originally Released Under LGPL - original licence link has changed is not relivant.
14708 * <script type="text/javascript">
14713 * @class Roo.data.SortTypes
14715 * Defines the default sorting (casting?) comparison functions used when sorting data.
14717 Roo.data.SortTypes = {
14719 * Default sort that does nothing
14720 * @param {Mixed} s The value being converted
14721 * @return {Mixed} The comparison value
14723 none : function(s){
14728 * The regular expression used to strip tags
14732 stripTagsRE : /<\/?[^>]+>/gi,
14735 * Strips all HTML tags to sort on text only
14736 * @param {Mixed} s The value being converted
14737 * @return {String} The comparison value
14739 asText : function(s){
14740 return String(s).replace(this.stripTagsRE, "");
14744 * Strips all HTML tags to sort on text only - Case insensitive
14745 * @param {Mixed} s The value being converted
14746 * @return {String} The comparison value
14748 asUCText : function(s){
14749 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14753 * Case insensitive string
14754 * @param {Mixed} s The value being converted
14755 * @return {String} The comparison value
14757 asUCString : function(s) {
14758 return String(s).toUpperCase();
14763 * @param {Mixed} s The value being converted
14764 * @return {Number} The comparison value
14766 asDate : function(s) {
14770 if(s instanceof Date){
14771 return s.getTime();
14773 return Date.parse(String(s));
14778 * @param {Mixed} s The value being converted
14779 * @return {Float} The comparison value
14781 asFloat : function(s) {
14782 var val = parseFloat(String(s).replace(/,/g, ""));
14791 * @param {Mixed} s The value being converted
14792 * @return {Number} The comparison value
14794 asInt : function(s) {
14795 var val = parseInt(String(s).replace(/,/g, ""));
14803 * Ext JS Library 1.1.1
14804 * Copyright(c) 2006-2007, Ext JS, LLC.
14806 * Originally Released Under LGPL - original licence link has changed is not relivant.
14809 * <script type="text/javascript">
14813 * @class Roo.data.Record
14814 * Instances of this class encapsulate both record <em>definition</em> information, and record
14815 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14816 * to access Records cached in an {@link Roo.data.Store} object.<br>
14818 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14819 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14822 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14824 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14825 * {@link #create}. The parameters are the same.
14826 * @param {Array} data An associative Array of data values keyed by the field name.
14827 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14828 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14829 * not specified an integer id is generated.
14831 Roo.data.Record = function(data, id){
14832 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14837 * Generate a constructor for a specific record layout.
14838 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14839 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14840 * Each field definition object may contain the following properties: <ul>
14841 * <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,
14842 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14843 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14844 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14845 * is being used, then this is a string containing the javascript expression to reference the data relative to
14846 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14847 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14848 * this may be omitted.</p></li>
14849 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14850 * <ul><li>auto (Default, implies no conversion)</li>
14855 * <li>date</li></ul></p></li>
14856 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14857 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14858 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14859 * by the Reader into an object that will be stored in the Record. It is passed the
14860 * following parameters:<ul>
14861 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14863 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14865 * <br>usage:<br><pre><code>
14866 var TopicRecord = Roo.data.Record.create(
14867 {name: 'title', mapping: 'topic_title'},
14868 {name: 'author', mapping: 'username'},
14869 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14870 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14871 {name: 'lastPoster', mapping: 'user2'},
14872 {name: 'excerpt', mapping: 'post_text'}
14875 var myNewRecord = new TopicRecord({
14876 title: 'Do my job please',
14879 lastPost: new Date(),
14880 lastPoster: 'Animal',
14881 excerpt: 'No way dude!'
14883 myStore.add(myNewRecord);
14888 Roo.data.Record.create = function(o){
14889 var f = function(){
14890 f.superclass.constructor.apply(this, arguments);
14892 Roo.extend(f, Roo.data.Record);
14893 var p = f.prototype;
14894 p.fields = new Roo.util.MixedCollection(false, function(field){
14897 for(var i = 0, len = o.length; i < len; i++){
14898 p.fields.add(new Roo.data.Field(o[i]));
14900 f.getField = function(name){
14901 return p.fields.get(name);
14906 Roo.data.Record.AUTO_ID = 1000;
14907 Roo.data.Record.EDIT = 'edit';
14908 Roo.data.Record.REJECT = 'reject';
14909 Roo.data.Record.COMMIT = 'commit';
14911 Roo.data.Record.prototype = {
14913 * Readonly flag - true if this record has been modified.
14922 join : function(store){
14923 this.store = store;
14927 * Set the named field to the specified value.
14928 * @param {String} name The name of the field to set.
14929 * @param {Object} value The value to set the field to.
14931 set : function(name, value){
14932 if(this.data[name] == value){
14936 if(!this.modified){
14937 this.modified = {};
14939 if(typeof this.modified[name] == 'undefined'){
14940 this.modified[name] = this.data[name];
14942 this.data[name] = value;
14943 if(!this.editing && this.store){
14944 this.store.afterEdit(this);
14949 * Get the value of the named field.
14950 * @param {String} name The name of the field to get the value of.
14951 * @return {Object} The value of the field.
14953 get : function(name){
14954 return this.data[name];
14958 beginEdit : function(){
14959 this.editing = true;
14960 this.modified = {};
14964 cancelEdit : function(){
14965 this.editing = false;
14966 delete this.modified;
14970 endEdit : function(){
14971 this.editing = false;
14972 if(this.dirty && this.store){
14973 this.store.afterEdit(this);
14978 * Usually called by the {@link Roo.data.Store} which owns the Record.
14979 * Rejects all changes made to the Record since either creation, or the last commit operation.
14980 * Modified fields are reverted to their original values.
14982 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14983 * of reject operations.
14985 reject : function(){
14986 var m = this.modified;
14988 if(typeof m[n] != "function"){
14989 this.data[n] = m[n];
14992 this.dirty = false;
14993 delete this.modified;
14994 this.editing = false;
14996 this.store.afterReject(this);
15001 * Usually called by the {@link Roo.data.Store} which owns the Record.
15002 * Commits all changes made to the Record since either creation, or the last commit operation.
15004 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15005 * of commit operations.
15007 commit : function(){
15008 this.dirty = false;
15009 delete this.modified;
15010 this.editing = false;
15012 this.store.afterCommit(this);
15017 hasError : function(){
15018 return this.error != null;
15022 clearError : function(){
15027 * Creates a copy of this record.
15028 * @param {String} id (optional) A new record id if you don't want to use this record's id
15031 copy : function(newId) {
15032 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15036 * Ext JS Library 1.1.1
15037 * Copyright(c) 2006-2007, Ext JS, LLC.
15039 * Originally Released Under LGPL - original licence link has changed is not relivant.
15042 * <script type="text/javascript">
15048 * @class Roo.data.Store
15049 * @extends Roo.util.Observable
15050 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15051 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15053 * 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
15054 * has no knowledge of the format of the data returned by the Proxy.<br>
15056 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15057 * instances from the data object. These records are cached and made available through accessor functions.
15059 * Creates a new Store.
15060 * @param {Object} config A config object containing the objects needed for the Store to access data,
15061 * and read the data into Records.
15063 Roo.data.Store = function(config){
15064 this.data = new Roo.util.MixedCollection(false);
15065 this.data.getKey = function(o){
15068 this.baseParams = {};
15070 this.paramNames = {
15075 "multisort" : "_multisort"
15078 if(config && config.data){
15079 this.inlineData = config.data;
15080 delete config.data;
15083 Roo.apply(this, config);
15085 if(this.reader){ // reader passed
15086 this.reader = Roo.factory(this.reader, Roo.data);
15087 this.reader.xmodule = this.xmodule || false;
15088 if(!this.recordType){
15089 this.recordType = this.reader.recordType;
15091 if(this.reader.onMetaChange){
15092 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15096 if(this.recordType){
15097 this.fields = this.recordType.prototype.fields;
15099 this.modified = [];
15103 * @event datachanged
15104 * Fires when the data cache has changed, and a widget which is using this Store
15105 * as a Record cache should refresh its view.
15106 * @param {Store} this
15108 datachanged : true,
15110 * @event metachange
15111 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15112 * @param {Store} this
15113 * @param {Object} meta The JSON metadata
15118 * Fires when Records have been added to the Store
15119 * @param {Store} this
15120 * @param {Roo.data.Record[]} records The array of Records added
15121 * @param {Number} index The index at which the record(s) were added
15126 * Fires when a Record has been removed from the Store
15127 * @param {Store} this
15128 * @param {Roo.data.Record} record The Record that was removed
15129 * @param {Number} index The index at which the record was removed
15134 * Fires when a Record has been updated
15135 * @param {Store} this
15136 * @param {Roo.data.Record} record The Record that was updated
15137 * @param {String} operation The update operation being performed. Value may be one of:
15139 Roo.data.Record.EDIT
15140 Roo.data.Record.REJECT
15141 Roo.data.Record.COMMIT
15147 * Fires when the data cache has been cleared.
15148 * @param {Store} this
15152 * @event beforeload
15153 * Fires before a request is made for a new data object. If the beforeload handler returns false
15154 * the load action will be canceled.
15155 * @param {Store} this
15156 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15160 * @event beforeloadadd
15161 * Fires after a new set of Records has been loaded.
15162 * @param {Store} this
15163 * @param {Roo.data.Record[]} records The Records that were loaded
15164 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15166 beforeloadadd : true,
15169 * Fires after a new set of Records has been loaded, before they are added to the store.
15170 * @param {Store} this
15171 * @param {Roo.data.Record[]} records The Records that were loaded
15172 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15173 * @params {Object} return from reader
15177 * @event loadexception
15178 * Fires if an exception occurs in the Proxy during loading.
15179 * Called with the signature of the Proxy's "loadexception" event.
15180 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15183 * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
15184 * @param {Object} opts - load Options
15185 * @param {Object} jsonData from your request (normally this contains the Exception)
15187 loadexception : true
15191 this.proxy = Roo.factory(this.proxy, Roo.data);
15192 this.proxy.xmodule = this.xmodule || false;
15193 this.relayEvents(this.proxy, ["loadexception"]);
15195 this.sortToggle = {};
15196 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15198 Roo.data.Store.superclass.constructor.call(this);
15200 if(this.inlineData){
15201 this.loadData(this.inlineData);
15202 delete this.inlineData;
15206 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15208 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15209 * without a remote query - used by combo/forms at present.
15213 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15216 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15219 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15220 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15223 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15224 * on any HTTP request
15227 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15230 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15234 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15235 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15237 remoteSort : false,
15240 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15241 * loaded or when a record is removed. (defaults to false).
15243 pruneModifiedRecords : false,
15246 lastOptions : null,
15249 * Add Records to the Store and fires the add event.
15250 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15252 add : function(records){
15253 records = [].concat(records);
15254 for(var i = 0, len = records.length; i < len; i++){
15255 records[i].join(this);
15257 var index = this.data.length;
15258 this.data.addAll(records);
15259 this.fireEvent("add", this, records, index);
15263 * Remove a Record from the Store and fires the remove event.
15264 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15266 remove : function(record){
15267 var index = this.data.indexOf(record);
15268 this.data.removeAt(index);
15270 if(this.pruneModifiedRecords){
15271 this.modified.remove(record);
15273 this.fireEvent("remove", this, record, index);
15277 * Remove all Records from the Store and fires the clear event.
15279 removeAll : function(){
15281 if(this.pruneModifiedRecords){
15282 this.modified = [];
15284 this.fireEvent("clear", this);
15288 * Inserts Records to the Store at the given index and fires the add event.
15289 * @param {Number} index The start index at which to insert the passed Records.
15290 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15292 insert : function(index, records){
15293 records = [].concat(records);
15294 for(var i = 0, len = records.length; i < len; i++){
15295 this.data.insert(index, records[i]);
15296 records[i].join(this);
15298 this.fireEvent("add", this, records, index);
15302 * Get the index within the cache of the passed Record.
15303 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15304 * @return {Number} The index of the passed Record. Returns -1 if not found.
15306 indexOf : function(record){
15307 return this.data.indexOf(record);
15311 * Get the index within the cache of the Record with the passed id.
15312 * @param {String} id The id of the Record to find.
15313 * @return {Number} The index of the Record. Returns -1 if not found.
15315 indexOfId : function(id){
15316 return this.data.indexOfKey(id);
15320 * Get the Record with the specified id.
15321 * @param {String} id The id of the Record to find.
15322 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15324 getById : function(id){
15325 return this.data.key(id);
15329 * Get the Record at the specified index.
15330 * @param {Number} index The index of the Record to find.
15331 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15333 getAt : function(index){
15334 return this.data.itemAt(index);
15338 * Returns a range of Records between specified indices.
15339 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15340 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15341 * @return {Roo.data.Record[]} An array of Records
15343 getRange : function(start, end){
15344 return this.data.getRange(start, end);
15348 storeOptions : function(o){
15349 o = Roo.apply({}, o);
15352 this.lastOptions = o;
15356 * Loads the Record cache from the configured Proxy using the configured Reader.
15358 * If using remote paging, then the first load call must specify the <em>start</em>
15359 * and <em>limit</em> properties in the options.params property to establish the initial
15360 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15362 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15363 * and this call will return before the new data has been loaded. Perform any post-processing
15364 * in a callback function, or in a "load" event handler.</strong>
15366 * @param {Object} options An object containing properties which control loading options:<ul>
15367 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15368 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15371 data : data, // array of key=>value data like JsonReader
15372 total : data.length,
15378 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15379 * passed the following arguments:<ul>
15380 * <li>r : Roo.data.Record[]</li>
15381 * <li>options: Options object from the load call</li>
15382 * <li>success: Boolean success indicator</li></ul></li>
15383 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15384 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15387 load : function(options){
15388 options = options || {};
15389 if(this.fireEvent("beforeload", this, options) !== false){
15390 this.storeOptions(options);
15391 var p = Roo.apply(options.params || {}, this.baseParams);
15392 // if meta was not loaded from remote source.. try requesting it.
15393 if (!this.reader.metaFromRemote) {
15394 p._requestMeta = 1;
15396 if(this.sortInfo && this.remoteSort){
15397 var pn = this.paramNames;
15398 p[pn["sort"]] = this.sortInfo.field;
15399 p[pn["dir"]] = this.sortInfo.direction;
15401 if (this.multiSort) {
15402 var pn = this.paramNames;
15403 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15406 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15411 * Reloads the Record cache from the configured Proxy using the configured Reader and
15412 * the options from the last load operation performed.
15413 * @param {Object} options (optional) An object containing properties which may override the options
15414 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15415 * the most recently used options are reused).
15417 reload : function(options){
15418 this.load(Roo.applyIf(options||{}, this.lastOptions));
15422 // Called as a callback by the Reader during a load operation.
15423 loadRecords : function(o, options, success){
15426 if(success !== false){
15427 this.fireEvent("load", this, [], options, o);
15429 if(options.callback){
15430 options.callback.call(options.scope || this, [], options, false);
15434 // if data returned failure - throw an exception.
15435 if (o.success === false) {
15436 // show a message if no listener is registered.
15437 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15438 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15440 // loadmask wil be hooked into this..
15441 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15444 var r = o.records, t = o.totalRecords || r.length;
15446 this.fireEvent("beforeloadadd", this, r, options, o);
15448 if(!options || options.add !== true){
15449 if(this.pruneModifiedRecords){
15450 this.modified = [];
15452 for(var i = 0, len = r.length; i < len; i++){
15456 this.data = this.snapshot;
15457 delete this.snapshot;
15460 this.data.addAll(r);
15461 this.totalLength = t;
15463 this.fireEvent("datachanged", this);
15465 this.totalLength = Math.max(t, this.data.length+r.length);
15469 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15471 var e = new Roo.data.Record({});
15473 e.set(this.parent.displayField, this.parent.emptyTitle);
15474 e.set(this.parent.valueField, '');
15479 this.fireEvent("load", this, r, options, o);
15480 if(options.callback){
15481 options.callback.call(options.scope || this, r, options, true);
15487 * Loads data from a passed data block. A Reader which understands the format of the data
15488 * must have been configured in the constructor.
15489 * @param {Object} data The data block from which to read the Records. The format of the data expected
15490 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15491 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15493 loadData : function(o, append){
15494 var r = this.reader.readRecords(o);
15495 this.loadRecords(r, {add: append}, true);
15499 * using 'cn' the nested child reader read the child array into it's child stores.
15500 * @param {Object} rec The record with a 'children array
15502 loadDataFromChildren : function(rec)
15504 this.loadData(this.reader.toLoadData(rec));
15509 * Gets the number of cached records.
15511 * <em>If using paging, this may not be the total size of the dataset. If the data object
15512 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15513 * the data set size</em>
15515 getCount : function(){
15516 return this.data.length || 0;
15520 * Gets the total number of records in the dataset as returned by the server.
15522 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15523 * the dataset size</em>
15525 getTotalCount : function(){
15526 return this.totalLength || 0;
15530 * Returns the sort state of the Store as an object with two properties:
15532 field {String} The name of the field by which the Records are sorted
15533 direction {String} The sort order, "ASC" or "DESC"
15536 getSortState : function(){
15537 return this.sortInfo;
15541 applySort : function(){
15542 if(this.sortInfo && !this.remoteSort){
15543 var s = this.sortInfo, f = s.field;
15544 var st = this.fields.get(f).sortType;
15545 var fn = function(r1, r2){
15546 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15547 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15549 this.data.sort(s.direction, fn);
15550 if(this.snapshot && this.snapshot != this.data){
15551 this.snapshot.sort(s.direction, fn);
15557 * Sets the default sort column and order to be used by the next load operation.
15558 * @param {String} fieldName The name of the field to sort by.
15559 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15561 setDefaultSort : function(field, dir){
15562 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15566 * Sort the Records.
15567 * If remote sorting is used, the sort is performed on the server, and the cache is
15568 * reloaded. If local sorting is used, the cache is sorted internally.
15569 * @param {String} fieldName The name of the field to sort by.
15570 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15572 sort : function(fieldName, dir){
15573 var f = this.fields.get(fieldName);
15575 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15577 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15578 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15583 this.sortToggle[f.name] = dir;
15584 this.sortInfo = {field: f.name, direction: dir};
15585 if(!this.remoteSort){
15587 this.fireEvent("datachanged", this);
15589 this.load(this.lastOptions);
15594 * Calls the specified function for each of the Records in the cache.
15595 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15596 * Returning <em>false</em> aborts and exits the iteration.
15597 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15599 each : function(fn, scope){
15600 this.data.each(fn, scope);
15604 * Gets all records modified since the last commit. Modified records are persisted across load operations
15605 * (e.g., during paging).
15606 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15608 getModifiedRecords : function(){
15609 return this.modified;
15613 createFilterFn : function(property, value, anyMatch){
15614 if(!value.exec){ // not a regex
15615 value = String(value);
15616 if(value.length == 0){
15619 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15621 return function(r){
15622 return value.test(r.data[property]);
15627 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15628 * @param {String} property A field on your records
15629 * @param {Number} start The record index to start at (defaults to 0)
15630 * @param {Number} end The last record index to include (defaults to length - 1)
15631 * @return {Number} The sum
15633 sum : function(property, start, end){
15634 var rs = this.data.items, v = 0;
15635 start = start || 0;
15636 end = (end || end === 0) ? end : rs.length-1;
15638 for(var i = start; i <= end; i++){
15639 v += (rs[i].data[property] || 0);
15645 * Filter the records by a specified property.
15646 * @param {String} field A field on your records
15647 * @param {String/RegExp} value Either a string that the field
15648 * should start with or a RegExp to test against the field
15649 * @param {Boolean} anyMatch True to match any part not just the beginning
15651 filter : function(property, value, anyMatch){
15652 var fn = this.createFilterFn(property, value, anyMatch);
15653 return fn ? this.filterBy(fn) : this.clearFilter();
15657 * Filter by a function. The specified function will be called with each
15658 * record in this data source. If the function returns true the record is included,
15659 * otherwise it is filtered.
15660 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15661 * @param {Object} scope (optional) The scope of the function (defaults to this)
15663 filterBy : function(fn, scope){
15664 this.snapshot = this.snapshot || this.data;
15665 this.data = this.queryBy(fn, scope||this);
15666 this.fireEvent("datachanged", this);
15670 * Query the records by a specified property.
15671 * @param {String} field A field on your records
15672 * @param {String/RegExp} value Either a string that the field
15673 * should start with or a RegExp to test against the field
15674 * @param {Boolean} anyMatch True to match any part not just the beginning
15675 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15677 query : function(property, value, anyMatch){
15678 var fn = this.createFilterFn(property, value, anyMatch);
15679 return fn ? this.queryBy(fn) : this.data.clone();
15683 * Query by a function. The specified function will be called with each
15684 * record in this data source. If the function returns true the record is included
15686 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15687 * @param {Object} scope (optional) The scope of the function (defaults to this)
15688 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15690 queryBy : function(fn, scope){
15691 var data = this.snapshot || this.data;
15692 return data.filterBy(fn, scope||this);
15696 * Collects unique values for a particular dataIndex from this store.
15697 * @param {String} dataIndex The property to collect
15698 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15699 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15700 * @return {Array} An array of the unique values
15702 collect : function(dataIndex, allowNull, bypassFilter){
15703 var d = (bypassFilter === true && this.snapshot) ?
15704 this.snapshot.items : this.data.items;
15705 var v, sv, r = [], l = {};
15706 for(var i = 0, len = d.length; i < len; i++){
15707 v = d[i].data[dataIndex];
15709 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15718 * Revert to a view of the Record cache with no filtering applied.
15719 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15721 clearFilter : function(suppressEvent){
15722 if(this.snapshot && this.snapshot != this.data){
15723 this.data = this.snapshot;
15724 delete this.snapshot;
15725 if(suppressEvent !== true){
15726 this.fireEvent("datachanged", this);
15732 afterEdit : function(record){
15733 if(this.modified.indexOf(record) == -1){
15734 this.modified.push(record);
15736 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15740 afterReject : function(record){
15741 this.modified.remove(record);
15742 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15746 afterCommit : function(record){
15747 this.modified.remove(record);
15748 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15752 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15753 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15755 commitChanges : function(){
15756 var m = this.modified.slice(0);
15757 this.modified = [];
15758 for(var i = 0, len = m.length; i < len; i++){
15764 * Cancel outstanding changes on all changed records.
15766 rejectChanges : function(){
15767 var m = this.modified.slice(0);
15768 this.modified = [];
15769 for(var i = 0, len = m.length; i < len; i++){
15774 onMetaChange : function(meta, rtype, o){
15775 this.recordType = rtype;
15776 this.fields = rtype.prototype.fields;
15777 delete this.snapshot;
15778 this.sortInfo = meta.sortInfo || this.sortInfo;
15779 this.modified = [];
15780 this.fireEvent('metachange', this, this.reader.meta);
15783 moveIndex : function(data, type)
15785 var index = this.indexOf(data);
15787 var newIndex = index + type;
15791 this.insert(newIndex, data);
15796 * Ext JS Library 1.1.1
15797 * Copyright(c) 2006-2007, Ext JS, LLC.
15799 * Originally Released Under LGPL - original licence link has changed is not relivant.
15802 * <script type="text/javascript">
15806 * @class Roo.data.SimpleStore
15807 * @extends Roo.data.Store
15808 * Small helper class to make creating Stores from Array data easier.
15809 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15810 * @cfg {Array} fields An array of field definition objects, or field name strings.
15811 * @cfg {Object} an existing reader (eg. copied from another store)
15812 * @cfg {Array} data The multi-dimensional array of data
15813 * @cfg {Roo.data.DataProxy} proxy [not-required]
15814 * @cfg {Roo.data.Reader} reader [not-required]
15816 * @param {Object} config
15818 Roo.data.SimpleStore = function(config)
15820 Roo.data.SimpleStore.superclass.constructor.call(this, {
15822 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15825 Roo.data.Record.create(config.fields)
15827 proxy : new Roo.data.MemoryProxy(config.data)
15831 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15833 * Ext JS Library 1.1.1
15834 * Copyright(c) 2006-2007, Ext JS, LLC.
15836 * Originally Released Under LGPL - original licence link has changed is not relivant.
15839 * <script type="text/javascript">
15844 * @extends Roo.data.Store
15845 * @class Roo.data.JsonStore
15846 * Small helper class to make creating Stores for JSON data easier. <br/>
15848 var store = new Roo.data.JsonStore({
15849 url: 'get-images.php',
15851 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15854 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15855 * JsonReader and HttpProxy (unless inline data is provided).</b>
15856 * @cfg {Array} fields An array of field definition objects, or field name strings.
15858 * @param {Object} config
15860 Roo.data.JsonStore = function(c){
15861 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15862 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15863 reader: new Roo.data.JsonReader(c, c.fields)
15866 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15868 * Ext JS Library 1.1.1
15869 * Copyright(c) 2006-2007, Ext JS, LLC.
15871 * Originally Released Under LGPL - original licence link has changed is not relivant.
15874 * <script type="text/javascript">
15878 Roo.data.Field = function(config){
15879 if(typeof config == "string"){
15880 config = {name: config};
15882 Roo.apply(this, config);
15885 this.type = "auto";
15888 var st = Roo.data.SortTypes;
15889 // named sortTypes are supported, here we look them up
15890 if(typeof this.sortType == "string"){
15891 this.sortType = st[this.sortType];
15894 // set default sortType for strings and dates
15895 if(!this.sortType){
15898 this.sortType = st.asUCString;
15901 this.sortType = st.asDate;
15904 this.sortType = st.none;
15909 var stripRe = /[\$,%]/g;
15911 // prebuilt conversion function for this field, instead of
15912 // switching every time we're reading a value
15914 var cv, dateFormat = this.dateFormat;
15919 cv = function(v){ return v; };
15922 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15926 return v !== undefined && v !== null && v !== '' ?
15927 parseInt(String(v).replace(stripRe, ""), 10) : '';
15932 return v !== undefined && v !== null && v !== '' ?
15933 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15938 cv = function(v){ return v === true || v === "true" || v == 1; };
15945 if(v instanceof Date){
15949 if(dateFormat == "timestamp"){
15950 return new Date(v*1000);
15952 return Date.parseDate(v, dateFormat);
15954 var parsed = Date.parse(v);
15955 return parsed ? new Date(parsed) : null;
15964 Roo.data.Field.prototype = {
15972 * Ext JS Library 1.1.1
15973 * Copyright(c) 2006-2007, Ext JS, LLC.
15975 * Originally Released Under LGPL - original licence link has changed is not relivant.
15978 * <script type="text/javascript">
15981 // Base class for reading structured data from a data source. This class is intended to be
15982 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15985 * @class Roo.data.DataReader
15987 * Base class for reading structured data from a data source. This class is intended to be
15988 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15991 Roo.data.DataReader = function(meta, recordType){
15995 this.recordType = recordType instanceof Array ?
15996 Roo.data.Record.create(recordType) : recordType;
15999 Roo.data.DataReader.prototype = {
16002 readerType : 'Data',
16004 * Create an empty record
16005 * @param {Object} data (optional) - overlay some values
16006 * @return {Roo.data.Record} record created.
16008 newRow : function(d) {
16010 this.recordType.prototype.fields.each(function(c) {
16012 case 'int' : da[c.name] = 0; break;
16013 case 'date' : da[c.name] = new Date(); break;
16014 case 'float' : da[c.name] = 0.0; break;
16015 case 'boolean' : da[c.name] = false; break;
16016 default : da[c.name] = ""; break;
16020 return new this.recordType(Roo.apply(da, d));
16026 * Ext JS Library 1.1.1
16027 * Copyright(c) 2006-2007, Ext JS, LLC.
16029 * Originally Released Under LGPL - original licence link has changed is not relivant.
16032 * <script type="text/javascript">
16036 * @class Roo.data.DataProxy
16037 * @extends Roo.util.Observable
16039 * This class is an abstract base class for implementations which provide retrieval of
16040 * unformatted data objects.<br>
16042 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16043 * (of the appropriate type which knows how to parse the data object) to provide a block of
16044 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16046 * Custom implementations must implement the load method as described in
16047 * {@link Roo.data.HttpProxy#load}.
16049 Roo.data.DataProxy = function(){
16052 * @event beforeload
16053 * Fires before a network request is made to retrieve a data object.
16054 * @param {Object} This DataProxy object.
16055 * @param {Object} params The params parameter to the load function.
16060 * Fires before the load method's callback is called.
16061 * @param {Object} This DataProxy object.
16062 * @param {Object} o The data object.
16063 * @param {Object} arg The callback argument object passed to the load function.
16067 * @event loadexception
16068 * Fires if an Exception occurs during data retrieval.
16069 * @param {Object} This DataProxy object.
16070 * @param {Object} o The data object.
16071 * @param {Object} arg The callback argument object passed to the load function.
16072 * @param {Object} e The Exception.
16074 loadexception : true
16076 Roo.data.DataProxy.superclass.constructor.call(this);
16079 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16082 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16086 * Ext JS Library 1.1.1
16087 * Copyright(c) 2006-2007, Ext JS, LLC.
16089 * Originally Released Under LGPL - original licence link has changed is not relivant.
16092 * <script type="text/javascript">
16095 * @class Roo.data.MemoryProxy
16096 * @extends Roo.data.DataProxy
16097 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16098 * to the Reader when its load method is called.
16100 * @param {Object} config A config object containing the objects needed for the Store to access data,
16102 Roo.data.MemoryProxy = function(config){
16104 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16105 data = config.data;
16107 Roo.data.MemoryProxy.superclass.constructor.call(this);
16111 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16114 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16117 * Load data from the requested source (in this case an in-memory
16118 * data object passed to the constructor), read the data object into
16119 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16120 * process that block using the passed callback.
16121 * @param {Object} params This parameter is not used by the MemoryProxy class.
16122 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16123 * object into a block of Roo.data.Records.
16124 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16125 * The function must be passed <ul>
16126 * <li>The Record block object</li>
16127 * <li>The "arg" argument from the load function</li>
16128 * <li>A boolean success indicator</li>
16130 * @param {Object} scope The scope in which to call the callback
16131 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16133 load : function(params, reader, callback, scope, arg){
16134 params = params || {};
16137 result = reader.readRecords(params.data ? params.data :this.data);
16139 this.fireEvent("loadexception", this, arg, null, e);
16140 callback.call(scope, null, arg, false);
16143 callback.call(scope, result, arg, true);
16147 update : function(params, records){
16152 * Ext JS Library 1.1.1
16153 * Copyright(c) 2006-2007, Ext JS, LLC.
16155 * Originally Released Under LGPL - original licence link has changed is not relivant.
16158 * <script type="text/javascript">
16161 * @class Roo.data.HttpProxy
16162 * @extends Roo.data.DataProxy
16163 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16164 * configured to reference a certain URL.<br><br>
16166 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16167 * from which the running page was served.<br><br>
16169 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16171 * Be aware that to enable the browser to parse an XML document, the server must set
16172 * the Content-Type header in the HTTP response to "text/xml".
16174 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16175 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16176 * will be used to make the request.
16178 Roo.data.HttpProxy = function(conn){
16179 Roo.data.HttpProxy.superclass.constructor.call(this);
16180 // is conn a conn config or a real conn?
16182 this.useAjax = !conn || !conn.events;
16186 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16187 // thse are take from connection...
16190 * @cfg {String} url The default URL to be used for requests to the server. (defaults to undefined)
16193 * @cfg {Object} extraParams An object containing properties which are used as
16194 * extra parameters to each request made by this object. (defaults to undefined)
16197 * @cfg {Object} defaultHeaders An object containing request headers which are added
16198 * to each request made by this object. (defaults to undefined)
16201 * @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)
16204 * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
16207 * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
16213 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16217 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16218 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16219 * a finer-grained basis than the DataProxy events.
16221 getConnection : function(){
16222 return this.useAjax ? Roo.Ajax : this.conn;
16226 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16227 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16228 * process that block using the passed callback.
16229 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16230 * for the request to the remote server.
16231 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16232 * object into a block of Roo.data.Records.
16233 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16234 * The function must be passed <ul>
16235 * <li>The Record block object</li>
16236 * <li>The "arg" argument from the load function</li>
16237 * <li>A boolean success indicator</li>
16239 * @param {Object} scope The scope in which to call the callback
16240 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16242 load : function(params, reader, callback, scope, arg){
16243 if(this.fireEvent("beforeload", this, params) !== false){
16245 params : params || {},
16247 callback : callback,
16252 callback : this.loadResponse,
16256 Roo.applyIf(o, this.conn);
16257 if(this.activeRequest){
16258 Roo.Ajax.abort(this.activeRequest);
16260 this.activeRequest = Roo.Ajax.request(o);
16262 this.conn.request(o);
16265 callback.call(scope||this, null, arg, false);
16270 loadResponse : function(o, success, response){
16271 delete this.activeRequest;
16273 this.fireEvent("loadexception", this, o, response);
16274 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16279 result = o.reader.read(response);
16282 o.raw = { errorMsg : response.responseText };
16283 this.fireEvent("loadexception", this, o, response, e);
16284 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16288 this.fireEvent("load", this, o, o.request.arg);
16289 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16293 update : function(dataSet){
16298 updateResponse : function(dataSet){
16303 * Ext JS Library 1.1.1
16304 * Copyright(c) 2006-2007, Ext JS, LLC.
16306 * Originally Released Under LGPL - original licence link has changed is not relivant.
16309 * <script type="text/javascript">
16313 * @class Roo.data.ScriptTagProxy
16314 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16315 * other than the originating domain of the running page.<br><br>
16317 * <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
16318 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16320 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16321 * source code that is used as the source inside a <script> tag.<br><br>
16323 * In order for the browser to process the returned data, the server must wrap the data object
16324 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16325 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16326 * depending on whether the callback name was passed:
16329 boolean scriptTag = false;
16330 String cb = request.getParameter("callback");
16333 response.setContentType("text/javascript");
16335 response.setContentType("application/x-json");
16337 Writer out = response.getWriter();
16339 out.write(cb + "(");
16341 out.print(dataBlock.toJsonString());
16348 * @param {Object} config A configuration object.
16350 Roo.data.ScriptTagProxy = function(config){
16351 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16352 Roo.apply(this, config);
16353 this.head = document.getElementsByTagName("head")[0];
16356 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16358 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16360 * @cfg {String} url The URL from which to request the data object.
16363 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16367 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16368 * the server the name of the callback function set up by the load call to process the returned data object.
16369 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16370 * javascript output which calls this named function passing the data object as its only parameter.
16372 callbackParam : "callback",
16374 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16375 * name to the request.
16380 * Load data from the configured URL, read the data object into
16381 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16382 * process that block using the passed callback.
16383 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16384 * for the request to the remote server.
16385 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16386 * object into a block of Roo.data.Records.
16387 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16388 * The function must be passed <ul>
16389 * <li>The Record block object</li>
16390 * <li>The "arg" argument from the load function</li>
16391 * <li>A boolean success indicator</li>
16393 * @param {Object} scope The scope in which to call the callback
16394 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16396 load : function(params, reader, callback, scope, arg){
16397 if(this.fireEvent("beforeload", this, params) !== false){
16399 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16401 var url = this.url;
16402 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16404 url += "&_dc=" + (new Date().getTime());
16406 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16409 cb : "stcCallback"+transId,
16410 scriptId : "stcScript"+transId,
16414 callback : callback,
16420 window[trans.cb] = function(o){
16421 conn.handleResponse(o, trans);
16424 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16426 if(this.autoAbort !== false){
16430 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16432 var script = document.createElement("script");
16433 script.setAttribute("src", url);
16434 script.setAttribute("type", "text/javascript");
16435 script.setAttribute("id", trans.scriptId);
16436 this.head.appendChild(script);
16438 this.trans = trans;
16440 callback.call(scope||this, null, arg, false);
16445 isLoading : function(){
16446 return this.trans ? true : false;
16450 * Abort the current server request.
16452 abort : function(){
16453 if(this.isLoading()){
16454 this.destroyTrans(this.trans);
16459 destroyTrans : function(trans, isLoaded){
16460 this.head.removeChild(document.getElementById(trans.scriptId));
16461 clearTimeout(trans.timeoutId);
16463 window[trans.cb] = undefined;
16465 delete window[trans.cb];
16468 // if hasn't been loaded, wait for load to remove it to prevent script error
16469 window[trans.cb] = function(){
16470 window[trans.cb] = undefined;
16472 delete window[trans.cb];
16479 handleResponse : function(o, trans){
16480 this.trans = false;
16481 this.destroyTrans(trans, true);
16484 result = trans.reader.readRecords(o);
16486 this.fireEvent("loadexception", this, o, trans.arg, e);
16487 trans.callback.call(trans.scope||window, null, trans.arg, false);
16490 this.fireEvent("load", this, o, trans.arg);
16491 trans.callback.call(trans.scope||window, result, trans.arg, true);
16495 handleFailure : function(trans){
16496 this.trans = false;
16497 this.destroyTrans(trans, false);
16498 this.fireEvent("loadexception", this, null, trans.arg);
16499 trans.callback.call(trans.scope||window, null, trans.arg, false);
16503 * Ext JS Library 1.1.1
16504 * Copyright(c) 2006-2007, Ext JS, LLC.
16506 * Originally Released Under LGPL - original licence link has changed is not relivant.
16509 * <script type="text/javascript">
16513 * @class Roo.data.JsonReader
16514 * @extends Roo.data.DataReader
16515 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16516 * based on mappings in a provided Roo.data.Record constructor.
16518 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16519 * in the reply previously.
16524 var RecordDef = Roo.data.Record.create([
16525 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16526 {name: 'occupation'} // This field will use "occupation" as the mapping.
16528 var myReader = new Roo.data.JsonReader({
16529 totalProperty: "results", // The property which contains the total dataset size (optional)
16530 root: "rows", // The property which contains an Array of row objects
16531 id: "id" // The property within each row object that provides an ID for the record (optional)
16535 * This would consume a JSON file like this:
16537 { 'results': 2, 'rows': [
16538 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16539 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16542 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16543 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16544 * paged from the remote server.
16545 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16546 * @cfg {String} root name of the property which contains the Array of row objects.
16547 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16548 * @cfg {Array} fields Array of field definition objects
16550 * Create a new JsonReader
16551 * @param {Object} meta Metadata configuration options
16552 * @param {Object} recordType Either an Array of field definition objects,
16553 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16555 Roo.data.JsonReader = function(meta, recordType){
16558 // set some defaults:
16559 Roo.applyIf(meta, {
16560 totalProperty: 'total',
16561 successProperty : 'success',
16566 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16568 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16570 readerType : 'Json',
16573 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16574 * Used by Store query builder to append _requestMeta to params.
16577 metaFromRemote : false,
16579 * This method is only used by a DataProxy which has retrieved data from a remote server.
16580 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16581 * @return {Object} data A data block which is used by an Roo.data.Store object as
16582 * a cache of Roo.data.Records.
16584 read : function(response){
16585 var json = response.responseText;
16587 var o = /* eval:var:o */ eval("("+json+")");
16589 throw {message: "JsonReader.read: Json object not found"};
16595 this.metaFromRemote = true;
16596 this.meta = o.metaData;
16597 this.recordType = Roo.data.Record.create(o.metaData.fields);
16598 this.onMetaChange(this.meta, this.recordType, o);
16600 return this.readRecords(o);
16603 // private function a store will implement
16604 onMetaChange : function(meta, recordType, o){
16611 simpleAccess: function(obj, subsc) {
16618 getJsonAccessor: function(){
16620 return function(expr) {
16622 return(re.test(expr))
16623 ? new Function("obj", "return obj." + expr)
16628 return Roo.emptyFn;
16633 * Create a data block containing Roo.data.Records from an XML document.
16634 * @param {Object} o An object which contains an Array of row objects in the property specified
16635 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16636 * which contains the total size of the dataset.
16637 * @return {Object} data A data block which is used by an Roo.data.Store object as
16638 * a cache of Roo.data.Records.
16640 readRecords : function(o){
16642 * After any data loads, the raw JSON data is available for further custom processing.
16646 var s = this.meta, Record = this.recordType,
16647 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16649 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16651 if(s.totalProperty) {
16652 this.getTotal = this.getJsonAccessor(s.totalProperty);
16654 if(s.successProperty) {
16655 this.getSuccess = this.getJsonAccessor(s.successProperty);
16657 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16659 var g = this.getJsonAccessor(s.id);
16660 this.getId = function(rec) {
16662 return (r === undefined || r === "") ? null : r;
16665 this.getId = function(){return null;};
16668 for(var jj = 0; jj < fl; jj++){
16670 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16671 this.ef[jj] = this.getJsonAccessor(map);
16675 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16676 if(s.totalProperty){
16677 var vt = parseInt(this.getTotal(o), 10);
16682 if(s.successProperty){
16683 var vs = this.getSuccess(o);
16684 if(vs === false || vs === 'false'){
16689 for(var i = 0; i < c; i++){
16692 var id = this.getId(n);
16693 for(var j = 0; j < fl; j++){
16695 var v = this.ef[j](n);
16697 Roo.log('missing convert for ' + f.name);
16701 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16705 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16711 var record = new Record(values, id);
16713 records[i] = record;
16719 totalRecords : totalRecords
16722 // used when loading children.. @see loadDataFromChildren
16723 toLoadData: function(rec)
16725 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16726 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16727 return { data : data, total : data.length };
16732 * Ext JS Library 1.1.1
16733 * Copyright(c) 2006-2007, Ext JS, LLC.
16735 * Originally Released Under LGPL - original licence link has changed is not relivant.
16738 * <script type="text/javascript">
16742 * @class Roo.data.ArrayReader
16743 * @extends Roo.data.DataReader
16744 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16745 * Each element of that Array represents a row of data fields. The
16746 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16747 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16751 var RecordDef = Roo.data.Record.create([
16752 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16753 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16755 var myReader = new Roo.data.ArrayReader({
16756 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16760 * This would consume an Array like this:
16762 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16766 * Create a new JsonReader
16767 * @param {Object} meta Metadata configuration options.
16768 * @param {Object|Array} recordType Either an Array of field definition objects
16770 * @cfg {Array} fields Array of field definition objects
16771 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16772 * as specified to {@link Roo.data.Record#create},
16773 * or an {@link Roo.data.Record} object
16776 * created using {@link Roo.data.Record#create}.
16778 Roo.data.ArrayReader = function(meta, recordType)
16780 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16783 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16786 * Create a data block containing Roo.data.Records from an XML document.
16787 * @param {Object} o An Array of row objects which represents the dataset.
16788 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16789 * a cache of Roo.data.Records.
16791 readRecords : function(o)
16793 var sid = this.meta ? this.meta.id : null;
16794 var recordType = this.recordType, fields = recordType.prototype.fields;
16797 for(var i = 0; i < root.length; i++){
16800 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16801 for(var j = 0, jlen = fields.length; j < jlen; j++){
16802 var f = fields.items[j];
16803 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16804 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16806 values[f.name] = v;
16808 var record = new recordType(values, id);
16810 records[records.length] = record;
16814 totalRecords : records.length
16817 // used when loading children.. @see loadDataFromChildren
16818 toLoadData: function(rec)
16820 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16821 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16832 * @class Roo.bootstrap.form.ComboBox
16833 * @extends Roo.bootstrap.form.TriggerField
16834 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16835 * @cfg {Boolean} append (true|false) default false
16836 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16837 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16838 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16839 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16840 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16841 * @cfg {Boolean} animate default true
16842 * @cfg {Boolean} emptyResultText only for touch device
16843 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16844 * @cfg {String} emptyTitle default ''
16845 * @cfg {Number} width fixed with? experimental
16847 * Create a new ComboBox.
16848 * @param {Object} config Configuration options
16850 Roo.bootstrap.form.ComboBox = function(config){
16851 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16855 * Fires when the dropdown list is expanded
16856 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16861 * Fires when the dropdown list is collapsed
16862 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16866 * @event beforeselect
16867 * Fires before a list item is selected. Return false to cancel the selection.
16868 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869 * @param {Roo.data.Record} record The data record returned from the underlying store
16870 * @param {Number} index The index of the selected item in the dropdown list
16872 'beforeselect' : true,
16875 * Fires when a list item is selected
16876 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16878 * @param {Number} index The index of the selected item in the dropdown list
16882 * @event beforequery
16883 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16884 * The event object passed has these properties:
16885 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16886 * @param {String} query The query
16887 * @param {Boolean} forceAll true to force "all" query
16888 * @param {Boolean} cancel true to cancel the query
16889 * @param {Object} e The query event object
16891 'beforequery': true,
16894 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16895 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16900 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16901 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16907 * Fires when the remove value from the combobox array
16908 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16912 * @event afterremove
16913 * Fires when the remove value from the combobox array
16914 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16916 'afterremove' : true,
16918 * @event specialfilter
16919 * Fires when specialfilter
16920 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16922 'specialfilter' : true,
16925 * Fires when tick the element
16926 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16930 * @event touchviewdisplay
16931 * Fires when touch view require special display (default is using displayField)
16932 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16933 * @param {Object} cfg set html .
16935 'touchviewdisplay' : true
16940 this.tickItems = [];
16942 this.selectedIndex = -1;
16943 if(this.mode == 'local'){
16944 if(config.queryDelay === undefined){
16945 this.queryDelay = 10;
16947 if(config.minChars === undefined){
16953 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16956 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16957 * rendering into an Roo.Editor, defaults to false)
16960 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16961 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16964 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16967 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16968 * the dropdown list (defaults to undefined, with no header element)
16972 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16976 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16978 listWidth: undefined,
16980 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16981 * mode = 'remote' or 'text' if mode = 'local')
16983 displayField: undefined,
16986 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16987 * mode = 'remote' or 'value' if mode = 'local').
16988 * Note: use of a valueField requires the user make a selection
16989 * in order for a value to be mapped.
16991 valueField: undefined,
16993 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16998 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16999 * field's data value (defaults to the underlying DOM element's name)
17001 hiddenName: undefined,
17003 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17007 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17009 selectedClass: 'active',
17012 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17016 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17017 * anchor positions (defaults to 'tl-bl')
17019 listAlign: 'tl-bl?',
17021 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17025 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17026 * query specified by the allQuery config option (defaults to 'query')
17028 triggerAction: 'query',
17030 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17031 * (defaults to 4, does not apply if editable = false)
17035 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17036 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17040 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17041 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17045 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17046 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17050 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17051 * when editable = true (defaults to false)
17053 selectOnFocus:false,
17055 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17057 queryParam: 'query',
17059 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17060 * when mode = 'remote' (defaults to 'Loading...')
17062 loadingText: 'Loading...',
17064 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17068 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17072 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17073 * traditional select (defaults to true)
17077 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17081 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17085 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17086 * listWidth has a higher value)
17090 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17091 * allow the user to set arbitrary text into the field (defaults to false)
17093 forceSelection:false,
17095 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17096 * if typeAhead = true (defaults to 250)
17098 typeAheadDelay : 250,
17100 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17101 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17103 valueNotFoundText : undefined,
17105 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17107 blockFocus : false,
17110 * @cfg {Boolean} disableClear Disable showing of clear button.
17112 disableClear : false,
17114 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17116 alwaysQuery : false,
17119 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17124 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17126 invalidClass : "has-warning",
17129 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17131 validClass : "has-success",
17134 * @cfg {Boolean} specialFilter (true|false) special filter default false
17136 specialFilter : false,
17139 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17141 mobileTouchView : true,
17144 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17146 useNativeIOS : false,
17149 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17151 mobile_restrict_height : false,
17153 ios_options : false,
17165 btnPosition : 'right',
17166 triggerList : true,
17167 showToggleBtn : true,
17169 emptyResultText: 'Empty',
17170 triggerText : 'Select',
17174 // element that contains real text value.. (when hidden is used..)
17176 getAutoCreate : function()
17181 * Render classic select for iso
17184 if(Roo.isIOS && this.useNativeIOS){
17185 cfg = this.getAutoCreateNativeIOS();
17193 if(Roo.isTouch && this.mobileTouchView){
17194 cfg = this.getAutoCreateTouchView();
17201 if(!this.tickable){
17202 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17207 * ComboBox with tickable selections
17210 var align = this.labelAlign || this.parentLabelAlign();
17213 cls : 'form-group roo-combobox-tickable' //input-group
17216 var btn_text_select = '';
17217 var btn_text_done = '';
17218 var btn_text_cancel = '';
17220 if (this.btn_text_show) {
17221 btn_text_select = 'Select';
17222 btn_text_done = 'Done';
17223 btn_text_cancel = 'Cancel';
17228 cls : 'tickable-buttons',
17233 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17234 //html : this.triggerText
17235 html: btn_text_select
17241 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17243 html: btn_text_done
17249 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17251 html: btn_text_cancel
17257 buttons.cn.unshift({
17259 cls: 'roo-select2-search-field-input'
17265 Roo.each(buttons.cn, function(c){
17267 c.cls += ' btn-' + _this.size;
17270 if (_this.disabled) {
17277 style : 'display: contents',
17282 cls: 'form-hidden-field'
17286 cls: 'roo-select2-choices',
17290 cls: 'roo-select2-search-field',
17301 cls: 'roo-select2-container input-group roo-select2-container-multi',
17307 // cls: 'typeahead typeahead-long dropdown-menu',
17308 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17313 if(this.hasFeedback && !this.allowBlank){
17317 cls: 'glyphicon form-control-feedback'
17320 combobox.cn.push(feedback);
17327 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17328 tooltip : 'This field is required'
17331 if (this.allowBlank) {
17334 style : 'display:none'
17337 if (align ==='left' && this.fieldLabel.length) {
17339 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17346 cls : 'control-label col-form-label',
17347 html : this.fieldLabel
17359 var labelCfg = cfg.cn[1];
17360 var contentCfg = cfg.cn[2];
17363 if(this.indicatorpos == 'right'){
17369 cls : 'control-label col-form-label',
17373 html : this.fieldLabel
17389 labelCfg = cfg.cn[0];
17390 contentCfg = cfg.cn[1];
17394 if(this.labelWidth > 12){
17395 labelCfg.style = "width: " + this.labelWidth + 'px';
17397 if(this.width * 1 > 0){
17398 contentCfg.style = "width: " + this.width + 'px';
17400 if(this.labelWidth < 13 && this.labelmd == 0){
17401 this.labelmd = this.labelWidth;
17404 if(this.labellg > 0){
17405 labelCfg.cls += ' col-lg-' + this.labellg;
17406 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17409 if(this.labelmd > 0){
17410 labelCfg.cls += ' col-md-' + this.labelmd;
17411 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17414 if(this.labelsm > 0){
17415 labelCfg.cls += ' col-sm-' + this.labelsm;
17416 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17419 if(this.labelxs > 0){
17420 labelCfg.cls += ' col-xs-' + this.labelxs;
17421 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17425 } else if ( this.fieldLabel.length) {
17426 // Roo.log(" label");
17431 //cls : 'input-group-addon',
17432 html : this.fieldLabel
17437 if(this.indicatorpos == 'right'){
17441 //cls : 'input-group-addon',
17442 html : this.fieldLabel
17452 // Roo.log(" no label && no align");
17459 ['xs','sm','md','lg'].map(function(size){
17460 if (settings[size]) {
17461 cfg.cls += ' col-' + size + '-' + settings[size];
17469 _initEventsCalled : false,
17472 initEvents: function()
17474 if (this._initEventsCalled) { // as we call render... prevent looping...
17477 this._initEventsCalled = true;
17480 throw "can not find store for combo";
17483 this.indicator = this.indicatorEl();
17485 this.store = Roo.factory(this.store, Roo.data);
17486 this.store.parent = this;
17488 // if we are building from html. then this element is so complex, that we can not really
17489 // use the rendered HTML.
17490 // so we have to trash and replace the previous code.
17491 if (Roo.XComponent.build_from_html) {
17492 // remove this element....
17493 var e = this.el.dom, k=0;
17494 while (e ) { e = e.previousSibling; ++k;}
17499 this.rendered = false;
17501 this.render(this.parent().getChildContainer(true), k);
17504 if(Roo.isIOS && this.useNativeIOS){
17505 this.initIOSView();
17513 if(Roo.isTouch && this.mobileTouchView){
17514 this.initTouchView();
17519 this.initTickableEvents();
17523 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17525 if(this.hiddenName){
17527 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17529 this.hiddenField.dom.value =
17530 this.hiddenValue !== undefined ? this.hiddenValue :
17531 this.value !== undefined ? this.value : '';
17533 // prevent input submission
17534 this.el.dom.removeAttribute('name');
17535 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17540 // this.el.dom.setAttribute('autocomplete', 'off');
17543 var cls = 'x-combo-list';
17545 //this.list = new Roo.Layer({
17546 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17552 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17553 _this.list.setWidth(lw);
17556 this.list.on('mouseover', this.onViewOver, this);
17557 this.list.on('mousemove', this.onViewMove, this);
17558 this.list.on('scroll', this.onViewScroll, this);
17561 this.list.swallowEvent('mousewheel');
17562 this.assetHeight = 0;
17565 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17566 this.assetHeight += this.header.getHeight();
17569 this.innerList = this.list.createChild({cls:cls+'-inner'});
17570 this.innerList.on('mouseover', this.onViewOver, this);
17571 this.innerList.on('mousemove', this.onViewMove, this);
17572 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17574 if(this.allowBlank && !this.pageSize && !this.disableClear){
17575 this.footer = this.list.createChild({cls:cls+'-ft'});
17576 this.pageTb = new Roo.Toolbar(this.footer);
17580 this.footer = this.list.createChild({cls:cls+'-ft'});
17581 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17582 {pageSize: this.pageSize});
17586 if (this.pageTb && this.allowBlank && !this.disableClear) {
17588 this.pageTb.add(new Roo.Toolbar.Fill(), {
17589 cls: 'x-btn-icon x-btn-clear',
17591 handler: function()
17594 _this.clearValue();
17595 _this.onSelect(false, -1);
17600 this.assetHeight += this.footer.getHeight();
17605 this.tpl = Roo.bootstrap.version == 4 ?
17606 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17607 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17610 this.view = new Roo.View(this.list, this.tpl, {
17611 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17613 //this.view.wrapEl.setDisplayed(false);
17614 this.view.on('click', this.onViewClick, this);
17617 this.store.on('beforeload', this.onBeforeLoad, this);
17618 this.store.on('load', this.onLoad, this);
17619 this.store.on('loadexception', this.onLoadException, this);
17621 if(this.resizable){
17622 this.resizer = new Roo.Resizable(this.list, {
17623 pinned:true, handles:'se'
17625 this.resizer.on('resize', function(r, w, h){
17626 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17627 this.listWidth = w;
17628 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17629 this.restrictHeight();
17631 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17634 if(!this.editable){
17635 this.editable = true;
17636 this.setEditable(false);
17641 if (typeof(this.events.add.listeners) != 'undefined') {
17643 this.addicon = this.wrap.createChild(
17644 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17646 this.addicon.on('click', function(e) {
17647 this.fireEvent('add', this);
17650 if (typeof(this.events.edit.listeners) != 'undefined') {
17652 this.editicon = this.wrap.createChild(
17653 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17654 if (this.addicon) {
17655 this.editicon.setStyle('margin-left', '40px');
17657 this.editicon.on('click', function(e) {
17659 // we fire even if inothing is selected..
17660 this.fireEvent('edit', this, this.lastData );
17666 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17667 "up" : function(e){
17668 this.inKeyMode = true;
17672 "down" : function(e){
17673 if(!this.isExpanded()){
17674 this.onTriggerClick();
17676 this.inKeyMode = true;
17681 "enter" : function(e){
17682 // this.onViewClick();
17686 if(this.fireEvent("specialkey", this, e)){
17687 this.onViewClick(false);
17693 "esc" : function(e){
17697 "tab" : function(e){
17700 if(this.fireEvent("specialkey", this, e)){
17701 this.onViewClick(false);
17709 doRelay : function(foo, bar, hname){
17710 if(hname == 'down' || this.scope.isExpanded()){
17711 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17720 this.queryDelay = Math.max(this.queryDelay || 10,
17721 this.mode == 'local' ? 10 : 250);
17724 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17726 if(this.typeAhead){
17727 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17729 if(this.editable !== false){
17730 this.inputEl().on("keyup", this.onKeyUp, this);
17732 if(this.forceSelection){
17733 this.inputEl().on('blur', this.doForce, this);
17737 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17738 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17742 initTickableEvents: function()
17746 if(this.hiddenName){
17748 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17750 this.hiddenField.dom.value =
17751 this.hiddenValue !== undefined ? this.hiddenValue :
17752 this.value !== undefined ? this.value : '';
17754 // prevent input submission
17755 this.el.dom.removeAttribute('name');
17756 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17761 // this.list = this.el.select('ul.dropdown-menu',true).first();
17763 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17764 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17765 if(this.triggerList){
17766 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17769 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17770 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17772 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17773 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17775 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17776 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17778 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17779 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17780 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17783 this.cancelBtn.hide();
17788 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17789 _this.list.setWidth(lw);
17792 this.list.on('mouseover', this.onViewOver, this);
17793 this.list.on('mousemove', this.onViewMove, this);
17795 this.list.on('scroll', this.onViewScroll, this);
17798 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17799 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17802 this.view = new Roo.View(this.list, this.tpl, {
17807 selectedClass: this.selectedClass
17810 //this.view.wrapEl.setDisplayed(false);
17811 this.view.on('click', this.onViewClick, this);
17815 this.store.on('beforeload', this.onBeforeLoad, this);
17816 this.store.on('load', this.onLoad, this);
17817 this.store.on('loadexception', this.onLoadException, this);
17820 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17821 "up" : function(e){
17822 this.inKeyMode = true;
17826 "down" : function(e){
17827 this.inKeyMode = true;
17831 "enter" : function(e){
17832 if(this.fireEvent("specialkey", this, e)){
17833 this.onViewClick(false);
17839 "esc" : function(e){
17840 this.onTickableFooterButtonClick(e, false, false);
17843 "tab" : function(e){
17844 this.fireEvent("specialkey", this, e);
17846 this.onTickableFooterButtonClick(e, false, false);
17853 doRelay : function(e, fn, key){
17854 if(this.scope.isExpanded()){
17855 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17864 this.queryDelay = Math.max(this.queryDelay || 10,
17865 this.mode == 'local' ? 10 : 250);
17868 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17870 if(this.typeAhead){
17871 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17874 if(this.editable !== false){
17875 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17878 this.indicator = this.indicatorEl();
17880 if(this.indicator){
17881 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17882 this.indicator.hide();
17887 onDestroy : function(){
17889 this.view.setStore(null);
17890 this.view.el.removeAllListeners();
17891 this.view.el.remove();
17892 this.view.purgeListeners();
17895 this.list.dom.innerHTML = '';
17899 this.store.un('beforeload', this.onBeforeLoad, this);
17900 this.store.un('load', this.onLoad, this);
17901 this.store.un('loadexception', this.onLoadException, this);
17903 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17907 fireKey : function(e){
17908 if(e.isNavKeyPress() && !this.list.isVisible()){
17909 this.fireEvent("specialkey", this, e);
17914 onResize: function(w, h)
17918 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17920 // if(typeof w != 'number'){
17921 // // we do not handle it!?!?
17924 // var tw = this.trigger.getWidth();
17925 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17926 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17928 // this.inputEl().setWidth( this.adjustWidth('input', x));
17930 // //this.trigger.setStyle('left', x+'px');
17932 // if(this.list && this.listWidth === undefined){
17933 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17934 // this.list.setWidth(lw);
17935 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17943 * Allow or prevent the user from directly editing the field text. If false is passed,
17944 * the user will only be able to select from the items defined in the dropdown list. This method
17945 * is the runtime equivalent of setting the 'editable' config option at config time.
17946 * @param {Boolean} value True to allow the user to directly edit the field text
17948 setEditable : function(value){
17949 if(value == this.editable){
17952 this.editable = value;
17954 this.inputEl().dom.setAttribute('readOnly', true);
17955 this.inputEl().on('mousedown', this.onTriggerClick, this);
17956 this.inputEl().addClass('x-combo-noedit');
17958 this.inputEl().dom.removeAttribute('readOnly');
17959 this.inputEl().un('mousedown', this.onTriggerClick, this);
17960 this.inputEl().removeClass('x-combo-noedit');
17966 onBeforeLoad : function(combo,opts){
17967 if(!this.hasFocus){
17971 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17973 this.restrictHeight();
17974 this.selectedIndex = -1;
17978 onLoad : function(){
17980 this.hasQuery = false;
17982 if(!this.hasFocus){
17986 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17987 this.loading.hide();
17990 if(this.store.getCount() > 0){
17993 this.restrictHeight();
17994 if(this.lastQuery == this.allQuery){
17995 if(this.editable && !this.tickable){
17996 this.inputEl().dom.select();
18000 !this.selectByValue(this.value, true) &&
18003 !this.store.lastOptions ||
18004 typeof(this.store.lastOptions.add) == 'undefined' ||
18005 this.store.lastOptions.add != true
18008 this.select(0, true);
18011 if(this.autoFocus){
18014 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18015 this.taTask.delay(this.typeAheadDelay);
18019 this.onEmptyResults();
18025 onLoadException : function()
18027 this.hasQuery = false;
18029 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18030 this.loading.hide();
18033 if(this.tickable && this.editable){
18038 // only causes errors at present
18039 //Roo.log(this.store.reader.jsonData);
18040 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18042 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18048 onTypeAhead : function(){
18049 if(this.store.getCount() > 0){
18050 var r = this.store.getAt(0);
18051 var newValue = r.data[this.displayField];
18052 var len = newValue.length;
18053 var selStart = this.getRawValue().length;
18055 if(selStart != len){
18056 this.setRawValue(newValue);
18057 this.selectText(selStart, newValue.length);
18063 onSelect : function(record, index){
18065 if(this.fireEvent('beforeselect', this, record, index) !== false){
18067 this.setFromData(index > -1 ? record.data : false);
18070 this.fireEvent('select', this, record, index);
18075 * Returns the currently selected field value or empty string if no value is set.
18076 * @return {String} value The selected value
18078 getValue : function()
18080 if(Roo.isIOS && this.useNativeIOS){
18081 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18085 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18088 if(this.valueField){
18089 return typeof this.value != 'undefined' ? this.value : '';
18091 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18095 getRawValue : function()
18097 if(Roo.isIOS && this.useNativeIOS){
18098 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18101 var v = this.inputEl().getValue();
18107 * Clears any text/value currently set in the field
18109 clearValue : function(){
18111 if(this.hiddenField){
18112 this.hiddenField.dom.value = '';
18115 this.setRawValue('');
18116 this.lastSelectionText = '';
18117 this.lastData = false;
18119 var close = this.closeTriggerEl();
18130 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18131 * will be displayed in the field. If the value does not match the data value of an existing item,
18132 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18133 * Otherwise the field will be blank (although the value will still be set).
18134 * @param {String} value The value to match
18136 setValue : function(v)
18138 if(Roo.isIOS && this.useNativeIOS){
18139 this.setIOSValue(v);
18149 if(this.valueField){
18150 var r = this.findRecord(this.valueField, v);
18152 text = r.data[this.displayField];
18153 }else if(this.valueNotFoundText !== undefined){
18154 text = this.valueNotFoundText;
18157 this.lastSelectionText = text;
18158 if(this.hiddenField){
18159 this.hiddenField.dom.value = v;
18161 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18164 var close = this.closeTriggerEl();
18167 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18173 * @property {Object} the last set data for the element
18178 * Sets the value of the field based on a object which is related to the record format for the store.
18179 * @param {Object} value the value to set as. or false on reset?
18181 setFromData : function(o){
18188 var dv = ''; // display value
18189 var vv = ''; // value value..
18191 if (this.displayField) {
18192 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18194 // this is an error condition!!!
18195 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18198 if(this.valueField){
18199 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18202 var close = this.closeTriggerEl();
18205 if(dv.length || vv * 1 > 0){
18207 this.blockFocus=true;
18213 if(this.hiddenField){
18214 this.hiddenField.dom.value = vv;
18216 this.lastSelectionText = dv;
18217 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18221 // no hidden field.. - we store the value in 'value', but still display
18222 // display field!!!!
18223 this.lastSelectionText = dv;
18224 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18231 reset : function(){
18232 // overridden so that last data is reset..
18239 this.setValue(this.originalValue);
18240 //this.clearInvalid();
18241 this.lastData = false;
18243 this.view.clearSelections();
18249 findRecord : function(prop, value){
18251 if(this.store.getCount() > 0){
18252 this.store.each(function(r){
18253 if(r.data[prop] == value){
18263 getName: function()
18265 // returns hidden if it's set..
18266 if (!this.rendered) {return ''};
18267 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18271 onViewMove : function(e, t){
18272 this.inKeyMode = false;
18276 onViewOver : function(e, t){
18277 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18280 var item = this.view.findItemFromChild(t);
18283 var index = this.view.indexOf(item);
18284 this.select(index, false);
18289 onViewClick : function(view, doFocus, el, e)
18291 var index = this.view.getSelectedIndexes()[0];
18293 var r = this.store.getAt(index);
18297 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18304 Roo.each(this.tickItems, function(v,k){
18306 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18308 _this.tickItems.splice(k, 1);
18310 if(typeof(e) == 'undefined' && view == false){
18311 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18323 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18324 this.tickItems.push(r.data);
18327 if(typeof(e) == 'undefined' && view == false){
18328 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18335 this.onSelect(r, index);
18337 if(doFocus !== false && !this.blockFocus){
18338 this.inputEl().focus();
18343 restrictHeight : function(){
18344 //this.innerList.dom.style.height = '';
18345 //var inner = this.innerList.dom;
18346 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18347 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18348 //this.list.beginUpdate();
18349 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18350 this.list.alignTo(this.inputEl(), this.listAlign);
18351 this.list.alignTo(this.inputEl(), this.listAlign);
18352 //this.list.endUpdate();
18356 onEmptyResults : function(){
18358 if(this.tickable && this.editable){
18359 this.hasFocus = false;
18360 this.restrictHeight();
18368 * Returns true if the dropdown list is expanded, else false.
18370 isExpanded : function(){
18371 return this.list.isVisible();
18375 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18376 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18377 * @param {String} value The data value of the item to select
18378 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18379 * selected item if it is not currently in view (defaults to true)
18380 * @return {Boolean} True if the value matched an item in the list, else false
18382 selectByValue : function(v, scrollIntoView){
18383 if(v !== undefined && v !== null){
18384 var r = this.findRecord(this.valueField || this.displayField, v);
18386 this.select(this.store.indexOf(r), scrollIntoView);
18394 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18395 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18396 * @param {Number} index The zero-based index of the list item to select
18397 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18398 * selected item if it is not currently in view (defaults to true)
18400 select : function(index, scrollIntoView){
18401 this.selectedIndex = index;
18402 this.view.select(index);
18403 if(scrollIntoView !== false){
18404 var el = this.view.getNode(index);
18406 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18409 this.list.scrollChildIntoView(el, false);
18415 selectNext : function(){
18416 var ct = this.store.getCount();
18418 if(this.selectedIndex == -1){
18420 }else if(this.selectedIndex < ct-1){
18421 this.select(this.selectedIndex+1);
18427 selectPrev : function(){
18428 var ct = this.store.getCount();
18430 if(this.selectedIndex == -1){
18432 }else if(this.selectedIndex != 0){
18433 this.select(this.selectedIndex-1);
18439 onKeyUp : function(e){
18440 if(this.editable !== false && !e.isSpecialKey()){
18441 this.lastKey = e.getKey();
18442 this.dqTask.delay(this.queryDelay);
18447 validateBlur : function(){
18448 return !this.list || !this.list.isVisible();
18452 initQuery : function(){
18454 var v = this.getRawValue();
18456 if(this.tickable && this.editable){
18457 v = this.tickableInputEl().getValue();
18464 doForce : function(){
18465 if(this.inputEl().dom.value.length > 0){
18466 this.inputEl().dom.value =
18467 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18473 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18474 * query allowing the query action to be canceled if needed.
18475 * @param {String} query The SQL query to execute
18476 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18477 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18478 * saved in the current store (defaults to false)
18480 doQuery : function(q, forceAll){
18482 if(q === undefined || q === null){
18487 forceAll: forceAll,
18491 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18496 forceAll = qe.forceAll;
18497 if(forceAll === true || (q.length >= this.minChars)){
18499 this.hasQuery = true;
18501 if(this.lastQuery != q || this.alwaysQuery){
18502 this.lastQuery = q;
18503 if(this.mode == 'local'){
18504 this.selectedIndex = -1;
18506 this.store.clearFilter();
18509 if(this.specialFilter){
18510 this.fireEvent('specialfilter', this);
18515 this.store.filter(this.displayField, q);
18518 this.store.fireEvent("datachanged", this.store);
18525 this.store.baseParams[this.queryParam] = q;
18527 var options = {params : this.getParams(q)};
18530 options.add = true;
18531 options.params.start = this.page * this.pageSize;
18534 this.store.load(options);
18537 * this code will make the page width larger, at the beginning, the list not align correctly,
18538 * we should expand the list on onLoad
18539 * so command out it
18544 this.selectedIndex = -1;
18549 this.loadNext = false;
18553 getParams : function(q){
18555 //p[this.queryParam] = q;
18559 p.limit = this.pageSize;
18565 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18567 collapse : function(){
18568 if(!this.isExpanded()){
18574 this.hasFocus = false;
18578 this.cancelBtn.hide();
18579 this.trigger.show();
18582 this.tickableInputEl().dom.value = '';
18583 this.tickableInputEl().blur();
18588 Roo.get(document).un('mousedown', this.collapseIf, this);
18589 Roo.get(document).un('mousewheel', this.collapseIf, this);
18590 if (!this.editable) {
18591 Roo.get(document).un('keydown', this.listKeyPress, this);
18593 this.fireEvent('collapse', this);
18599 collapseIf : function(e){
18600 var in_combo = e.within(this.el);
18601 var in_list = e.within(this.list);
18602 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18604 if (in_combo || in_list || is_list) {
18605 //e.stopPropagation();
18610 this.onTickableFooterButtonClick(e, false, false);
18618 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18620 expand : function(){
18622 if(this.isExpanded() || !this.hasFocus){
18626 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18627 this.list.setWidth(lw);
18633 this.restrictHeight();
18637 this.tickItems = Roo.apply([], this.item);
18640 this.cancelBtn.show();
18641 this.trigger.hide();
18644 this.tickableInputEl().focus();
18649 Roo.get(document).on('mousedown', this.collapseIf, this);
18650 Roo.get(document).on('mousewheel', this.collapseIf, this);
18651 if (!this.editable) {
18652 Roo.get(document).on('keydown', this.listKeyPress, this);
18655 this.fireEvent('expand', this);
18659 // Implements the default empty TriggerField.onTriggerClick function
18660 onTriggerClick : function(e)
18662 Roo.log('trigger click');
18664 if(this.disabled || !this.triggerList){
18669 this.loadNext = false;
18671 if(this.isExpanded()){
18673 if (!this.blockFocus) {
18674 this.inputEl().focus();
18678 this.hasFocus = true;
18679 if(this.triggerAction == 'all') {
18680 this.doQuery(this.allQuery, true);
18682 this.doQuery(this.getRawValue());
18684 if (!this.blockFocus) {
18685 this.inputEl().focus();
18690 onTickableTriggerClick : function(e)
18697 this.loadNext = false;
18698 this.hasFocus = true;
18700 if(this.triggerAction == 'all') {
18701 this.doQuery(this.allQuery, true);
18703 this.doQuery(this.getRawValue());
18707 onSearchFieldClick : function(e)
18709 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18710 this.onTickableFooterButtonClick(e, false, false);
18714 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18719 this.loadNext = false;
18720 this.hasFocus = true;
18722 if(this.triggerAction == 'all') {
18723 this.doQuery(this.allQuery, true);
18725 this.doQuery(this.getRawValue());
18729 listKeyPress : function(e)
18731 //Roo.log('listkeypress');
18732 // scroll to first matching element based on key pres..
18733 if (e.isSpecialKey()) {
18736 var k = String.fromCharCode(e.getKey()).toUpperCase();
18739 var csel = this.view.getSelectedNodes();
18740 var cselitem = false;
18742 var ix = this.view.indexOf(csel[0]);
18743 cselitem = this.store.getAt(ix);
18744 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18750 this.store.each(function(v) {
18752 // start at existing selection.
18753 if (cselitem.id == v.id) {
18759 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18760 match = this.store.indexOf(v);
18766 if (match === false) {
18767 return true; // no more action?
18770 this.view.select(match);
18771 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18772 sn.scrollIntoView(sn.dom.parentNode, false);
18775 onViewScroll : function(e, t){
18777 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){
18781 this.hasQuery = true;
18783 this.loading = this.list.select('.loading', true).first();
18785 if(this.loading === null){
18786 this.list.createChild({
18788 cls: 'loading roo-select2-more-results roo-select2-active',
18789 html: 'Loading more results...'
18792 this.loading = this.list.select('.loading', true).first();
18794 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18796 this.loading.hide();
18799 this.loading.show();
18804 this.loadNext = true;
18806 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18811 addItem : function(o)
18813 var dv = ''; // display value
18815 if (this.displayField) {
18816 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18818 // this is an error condition!!!
18819 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18826 var choice = this.choices.createChild({
18828 cls: 'roo-select2-search-choice',
18837 cls: 'roo-select2-search-choice-close fa fa-times',
18842 }, this.searchField);
18844 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18846 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18854 this.inputEl().dom.value = '';
18859 onRemoveItem : function(e, _self, o)
18861 e.preventDefault();
18863 this.lastItem = Roo.apply([], this.item);
18865 var index = this.item.indexOf(o.data) * 1;
18868 Roo.log('not this item?!');
18872 this.item.splice(index, 1);
18877 this.fireEvent('remove', this, e);
18883 syncValue : function()
18885 if(!this.item.length){
18892 Roo.each(this.item, function(i){
18893 if(_this.valueField){
18894 value.push(i[_this.valueField]);
18901 this.value = value.join(',');
18903 if(this.hiddenField){
18904 this.hiddenField.dom.value = this.value;
18907 this.store.fireEvent("datachanged", this.store);
18912 clearItem : function()
18914 if(!this.multiple){
18920 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18928 if(this.tickable && !Roo.isTouch){
18929 this.view.refresh();
18933 inputEl: function ()
18935 if(Roo.isIOS && this.useNativeIOS){
18936 return this.el.select('select.roo-ios-select', true).first();
18939 if(Roo.isTouch && this.mobileTouchView){
18940 return this.el.select('input.form-control',true).first();
18944 return this.searchField;
18947 return this.el.select('input.form-control',true).first();
18950 onTickableFooterButtonClick : function(e, btn, el)
18952 e.preventDefault();
18954 this.lastItem = Roo.apply([], this.item);
18956 if(btn && btn.name == 'cancel'){
18957 this.tickItems = Roo.apply([], this.item);
18966 Roo.each(this.tickItems, function(o){
18974 validate : function()
18976 if(this.getVisibilityEl().hasClass('hidden')){
18980 var v = this.getRawValue();
18983 v = this.getValue();
18986 if(this.disabled || this.allowBlank || v.length){
18991 this.markInvalid();
18995 tickableInputEl : function()
18997 if(!this.tickable || !this.editable){
18998 return this.inputEl();
19001 return this.inputEl().select('.roo-select2-search-field-input', true).first();
19005 getAutoCreateTouchView : function()
19010 cls: 'form-group' //input-group
19016 type : this.inputType,
19017 cls : 'form-control x-combo-noedit',
19018 autocomplete: 'new-password',
19019 placeholder : this.placeholder || '',
19024 input.name = this.name;
19028 input.cls += ' input-' + this.size;
19031 if (this.disabled) {
19032 input.disabled = true;
19036 cls : 'roo-combobox-wrap',
19043 inputblock.cls += ' input-group';
19045 inputblock.cn.unshift({
19047 cls : 'input-group-addon input-group-prepend input-group-text',
19052 if(this.removable && !this.multiple){
19053 inputblock.cls += ' roo-removable';
19055 inputblock.cn.push({
19058 cls : 'roo-combo-removable-btn close'
19062 if(this.hasFeedback && !this.allowBlank){
19064 inputblock.cls += ' has-feedback';
19066 inputblock.cn.push({
19068 cls: 'glyphicon form-control-feedback'
19075 inputblock.cls += (this.before) ? '' : ' input-group';
19077 inputblock.cn.push({
19079 cls : 'input-group-addon input-group-append input-group-text',
19085 var ibwrap = inputblock;
19090 cls: 'roo-select2-choices',
19094 cls: 'roo-select2-search-field',
19107 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19112 cls: 'form-hidden-field'
19118 if(!this.multiple && this.showToggleBtn){
19124 if (this.caret != false) {
19127 cls: 'fa fa-' + this.caret
19134 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19136 Roo.bootstrap.version == 3 ? caret : '',
19139 cls: 'combobox-clear',
19153 combobox.cls += ' roo-select2-container-multi';
19156 var required = this.allowBlank ? {
19158 style: 'display: none'
19161 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19162 tooltip : 'This field is required'
19165 var align = this.labelAlign || this.parentLabelAlign();
19167 if (align ==='left' && this.fieldLabel.length) {
19173 cls : 'control-label col-form-label',
19174 html : this.fieldLabel
19178 cls : 'roo-combobox-wrap ',
19185 var labelCfg = cfg.cn[1];
19186 var contentCfg = cfg.cn[2];
19189 if(this.indicatorpos == 'right'){
19194 cls : 'control-label col-form-label',
19198 html : this.fieldLabel
19204 cls : "roo-combobox-wrap ",
19212 labelCfg = cfg.cn[0];
19213 contentCfg = cfg.cn[1];
19218 if(this.labelWidth > 12){
19219 labelCfg.style = "width: " + this.labelWidth + 'px';
19222 if(this.labelWidth < 13 && this.labelmd == 0){
19223 this.labelmd = this.labelWidth;
19226 if(this.labellg > 0){
19227 labelCfg.cls += ' col-lg-' + this.labellg;
19228 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19231 if(this.labelmd > 0){
19232 labelCfg.cls += ' col-md-' + this.labelmd;
19233 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19236 if(this.labelsm > 0){
19237 labelCfg.cls += ' col-sm-' + this.labelsm;
19238 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19241 if(this.labelxs > 0){
19242 labelCfg.cls += ' col-xs-' + this.labelxs;
19243 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19247 } else if ( this.fieldLabel.length) {
19252 cls : 'control-label',
19253 html : this.fieldLabel
19264 if(this.indicatorpos == 'right'){
19268 cls : 'control-label',
19269 html : this.fieldLabel,
19287 var settings = this;
19289 ['xs','sm','md','lg'].map(function(size){
19290 if (settings[size]) {
19291 cfg.cls += ' col-' + size + '-' + settings[size];
19298 initTouchView : function()
19300 this.renderTouchView();
19302 this.touchViewEl.on('scroll', function(){
19303 this.el.dom.scrollTop = 0;
19306 this.originalValue = this.getValue();
19308 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19310 this.inputEl().on("click", this.showTouchView, this);
19311 if (this.triggerEl) {
19312 this.triggerEl.on("click", this.showTouchView, this);
19316 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19317 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19319 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19321 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19322 this.store.on('load', this.onTouchViewLoad, this);
19323 this.store.on('loadexception', this.onTouchViewLoadException, this);
19325 if(this.hiddenName){
19327 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19329 this.hiddenField.dom.value =
19330 this.hiddenValue !== undefined ? this.hiddenValue :
19331 this.value !== undefined ? this.value : '';
19333 this.el.dom.removeAttribute('name');
19334 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19338 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19339 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19342 if(this.removable && !this.multiple){
19343 var close = this.closeTriggerEl();
19345 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19346 close.on('click', this.removeBtnClick, this, close);
19350 * fix the bug in Safari iOS8
19352 this.inputEl().on("focus", function(e){
19353 document.activeElement.blur();
19356 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19363 renderTouchView : function()
19365 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19366 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19368 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19369 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19371 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19372 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19373 this.touchViewBodyEl.setStyle('overflow', 'auto');
19375 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19376 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19378 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19379 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19383 showTouchView : function()
19389 this.touchViewHeaderEl.hide();
19391 if(this.modalTitle.length){
19392 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19393 this.touchViewHeaderEl.show();
19396 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19397 this.touchViewEl.show();
19399 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19401 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19402 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19404 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19406 if(this.modalTitle.length){
19407 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19410 this.touchViewBodyEl.setHeight(bodyHeight);
19414 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19416 this.touchViewEl.addClass(['in','show']);
19419 if(this._touchViewMask){
19420 Roo.get(document.body).addClass("x-body-masked");
19421 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19422 this._touchViewMask.setStyle('z-index', 10000);
19423 this._touchViewMask.addClass('show');
19426 this.doTouchViewQuery();
19430 hideTouchView : function()
19432 this.touchViewEl.removeClass(['in','show']);
19436 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19438 this.touchViewEl.setStyle('display', 'none');
19441 if(this._touchViewMask){
19442 this._touchViewMask.removeClass('show');
19443 Roo.get(document.body).removeClass("x-body-masked");
19447 setTouchViewValue : function()
19454 Roo.each(this.tickItems, function(o){
19459 this.hideTouchView();
19462 doTouchViewQuery : function()
19471 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19475 if(!this.alwaysQuery || this.mode == 'local'){
19476 this.onTouchViewLoad();
19483 onTouchViewBeforeLoad : function(combo,opts)
19489 onTouchViewLoad : function()
19491 if(this.store.getCount() < 1){
19492 this.onTouchViewEmptyResults();
19496 this.clearTouchView();
19498 var rawValue = this.getRawValue();
19500 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19502 this.tickItems = [];
19504 this.store.data.each(function(d, rowIndex){
19505 var row = this.touchViewListGroup.createChild(template);
19507 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19508 row.addClass(d.data.cls);
19511 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19514 html : d.data[this.displayField]
19517 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19518 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19521 row.removeClass('selected');
19522 if(!this.multiple && this.valueField &&
19523 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19526 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19527 row.addClass('selected');
19530 if(this.multiple && this.valueField &&
19531 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19535 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19536 this.tickItems.push(d.data);
19539 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19543 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19545 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19547 if(this.modalTitle.length){
19548 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19551 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19553 if(this.mobile_restrict_height && listHeight < bodyHeight){
19554 this.touchViewBodyEl.setHeight(listHeight);
19559 if(firstChecked && listHeight > bodyHeight){
19560 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19565 onTouchViewLoadException : function()
19567 this.hideTouchView();
19570 onTouchViewEmptyResults : function()
19572 this.clearTouchView();
19574 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19576 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19580 clearTouchView : function()
19582 this.touchViewListGroup.dom.innerHTML = '';
19585 onTouchViewClick : function(e, el, o)
19587 e.preventDefault();
19590 var rowIndex = o.rowIndex;
19592 var r = this.store.getAt(rowIndex);
19594 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19596 if(!this.multiple){
19597 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19598 c.dom.removeAttribute('checked');
19601 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19603 this.setFromData(r.data);
19605 var close = this.closeTriggerEl();
19611 this.hideTouchView();
19613 this.fireEvent('select', this, r, rowIndex);
19618 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19619 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19620 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19624 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19625 this.addItem(r.data);
19626 this.tickItems.push(r.data);
19630 getAutoCreateNativeIOS : function()
19633 cls: 'form-group' //input-group,
19638 cls : 'roo-ios-select'
19642 combobox.name = this.name;
19645 if (this.disabled) {
19646 combobox.disabled = true;
19649 var settings = this;
19651 ['xs','sm','md','lg'].map(function(size){
19652 if (settings[size]) {
19653 cfg.cls += ' col-' + size + '-' + settings[size];
19663 initIOSView : function()
19665 this.store.on('load', this.onIOSViewLoad, this);
19670 onIOSViewLoad : function()
19672 if(this.store.getCount() < 1){
19676 this.clearIOSView();
19678 if(this.allowBlank) {
19680 var default_text = '-- SELECT --';
19682 if(this.placeholder.length){
19683 default_text = this.placeholder;
19686 if(this.emptyTitle.length){
19687 default_text += ' - ' + this.emptyTitle + ' -';
19690 var opt = this.inputEl().createChild({
19693 html : default_text
19697 o[this.valueField] = 0;
19698 o[this.displayField] = default_text;
19700 this.ios_options.push({
19707 this.store.data.each(function(d, rowIndex){
19711 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19712 html = d.data[this.displayField];
19717 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19718 value = d.data[this.valueField];
19727 if(this.value == d.data[this.valueField]){
19728 option['selected'] = true;
19731 var opt = this.inputEl().createChild(option);
19733 this.ios_options.push({
19740 this.inputEl().on('change', function(){
19741 this.fireEvent('select', this);
19746 clearIOSView: function()
19748 this.inputEl().dom.innerHTML = '';
19750 this.ios_options = [];
19753 setIOSValue: function(v)
19757 if(!this.ios_options){
19761 Roo.each(this.ios_options, function(opts){
19763 opts.el.dom.removeAttribute('selected');
19765 if(opts.data[this.valueField] != v){
19769 opts.el.dom.setAttribute('selected', true);
19775 * @cfg {Boolean} grow
19779 * @cfg {Number} growMin
19783 * @cfg {Number} growMax
19792 Roo.apply(Roo.bootstrap.form.ComboBox, {
19796 cls: 'modal-header',
19818 cls: 'list-group-item',
19822 cls: 'roo-combobox-list-group-item-value'
19826 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19840 listItemCheckbox : {
19842 cls: 'list-group-item',
19846 cls: 'roo-combobox-list-group-item-value'
19850 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19866 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19871 cls: 'modal-footer',
19879 cls: 'col-xs-6 text-left',
19882 cls: 'btn btn-danger roo-touch-view-cancel',
19888 cls: 'col-xs-6 text-right',
19891 cls: 'btn btn-success roo-touch-view-ok',
19902 Roo.apply(Roo.bootstrap.form.ComboBox, {
19904 touchViewTemplate : {
19906 cls: 'modal fade roo-combobox-touch-view',
19910 cls: 'modal-dialog',
19911 style : 'position:fixed', // we have to fix position....
19915 cls: 'modal-content',
19917 Roo.bootstrap.form.ComboBox.header,
19918 Roo.bootstrap.form.ComboBox.body,
19919 Roo.bootstrap.form.ComboBox.footer
19928 * Ext JS Library 1.1.1
19929 * Copyright(c) 2006-2007, Ext JS, LLC.
19931 * Originally Released Under LGPL - original licence link has changed is not relivant.
19934 * <script type="text/javascript">
19939 * @extends Roo.util.Observable
19940 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19941 * This class also supports single and multi selection modes. <br>
19942 * Create a data model bound view:
19944 var store = new Roo.data.Store(...);
19946 var view = new Roo.View({
19948 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19950 singleSelect: true,
19951 selectedClass: "ydataview-selected",
19955 // listen for node click?
19956 view.on("click", function(vw, index, node, e){
19957 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19961 dataModel.load("foobar.xml");
19963 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19965 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19966 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19968 * Note: old style constructor is still suported (container, template, config)
19971 * Create a new View
19972 * @param {Object} config The config object
19975 Roo.View = function(config, depreciated_tpl, depreciated_config){
19977 this.parent = false;
19979 if (typeof(depreciated_tpl) == 'undefined') {
19980 // new way.. - universal constructor.
19981 Roo.apply(this, config);
19982 this.el = Roo.get(this.el);
19985 this.el = Roo.get(config);
19986 this.tpl = depreciated_tpl;
19987 Roo.apply(this, depreciated_config);
19989 this.wrapEl = this.el.wrap().wrap();
19990 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19993 if(typeof(this.tpl) == "string"){
19994 this.tpl = new Roo.Template(this.tpl);
19996 // support xtype ctors..
19997 this.tpl = new Roo.factory(this.tpl, Roo);
20001 this.tpl.compile();
20006 * @event beforeclick
20007 * Fires before a click is processed. Returns false to cancel the default action.
20008 * @param {Roo.View} this
20009 * @param {Number} index The index of the target node
20010 * @param {HTMLElement} node The target node
20011 * @param {Roo.EventObject} e The raw event object
20013 "beforeclick" : true,
20016 * Fires when a template node is clicked.
20017 * @param {Roo.View} this
20018 * @param {Number} index The index of the target node
20019 * @param {HTMLElement} node The target node
20020 * @param {Roo.EventObject} e The raw event object
20025 * Fires when a template node is double clicked.
20026 * @param {Roo.View} this
20027 * @param {Number} index The index of the target node
20028 * @param {HTMLElement} node The target node
20029 * @param {Roo.EventObject} e The raw event object
20033 * @event contextmenu
20034 * Fires when a template node is right clicked.
20035 * @param {Roo.View} this
20036 * @param {Number} index The index of the target node
20037 * @param {HTMLElement} node The target node
20038 * @param {Roo.EventObject} e The raw event object
20040 "contextmenu" : true,
20042 * @event selectionchange
20043 * Fires when the selected nodes change.
20044 * @param {Roo.View} this
20045 * @param {Array} selections Array of the selected nodes
20047 "selectionchange" : true,
20050 * @event beforeselect
20051 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20052 * @param {Roo.View} this
20053 * @param {HTMLElement} node The node to be selected
20054 * @param {Array} selections Array of currently selected nodes
20056 "beforeselect" : true,
20058 * @event preparedata
20059 * Fires on every row to render, to allow you to change the data.
20060 * @param {Roo.View} this
20061 * @param {Object} data to be rendered (change this)
20063 "preparedata" : true
20071 "click": this.onClick,
20072 "dblclick": this.onDblClick,
20073 "contextmenu": this.onContextMenu,
20077 this.selections = [];
20079 this.cmp = new Roo.CompositeElementLite([]);
20081 this.store = Roo.factory(this.store, Roo.data);
20082 this.setStore(this.store, true);
20085 if ( this.footer && this.footer.xtype) {
20087 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20089 this.footer.dataSource = this.store;
20090 this.footer.container = fctr;
20091 this.footer = Roo.factory(this.footer, Roo);
20092 fctr.insertFirst(this.el);
20094 // this is a bit insane - as the paging toolbar seems to detach the el..
20095 // dom.parentNode.parentNode.parentNode
20096 // they get detached?
20100 Roo.View.superclass.constructor.call(this);
20105 Roo.extend(Roo.View, Roo.util.Observable, {
20108 * @cfg {Roo.data.Store} store Data store to load data from.
20113 * @cfg {String|Roo.Element} el The container element.
20118 * @cfg {String|Roo.Template} tpl The template used by this View
20122 * @cfg {String} dataName the named area of the template to use as the data area
20123 * Works with domtemplates roo-name="name"
20127 * @cfg {String} selectedClass The css class to add to selected nodes
20129 selectedClass : "x-view-selected",
20131 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20136 * @cfg {String} text to display on mask (default Loading)
20140 * @cfg {Boolean} multiSelect Allow multiple selection
20142 multiSelect : false,
20144 * @cfg {Boolean} singleSelect Allow single selection
20146 singleSelect: false,
20149 * @cfg {Boolean} toggleSelect - selecting
20151 toggleSelect : false,
20154 * @cfg {Boolean} tickable - selecting
20159 * Returns the element this view is bound to.
20160 * @return {Roo.Element}
20162 getEl : function(){
20163 return this.wrapEl;
20169 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20171 refresh : function(){
20172 //Roo.log('refresh');
20175 // if we are using something like 'domtemplate', then
20176 // the what gets used is:
20177 // t.applySubtemplate(NAME, data, wrapping data..)
20178 // the outer template then get' applied with
20179 // the store 'extra data'
20180 // and the body get's added to the
20181 // roo-name="data" node?
20182 // <span class='roo-tpl-{name}'></span> ?????
20186 this.clearSelections();
20187 this.el.update("");
20189 var records = this.store.getRange();
20190 if(records.length < 1) {
20192 // is this valid?? = should it render a template??
20194 this.el.update(this.emptyText);
20198 if (this.dataName) {
20199 this.el.update(t.apply(this.store.meta)); //????
20200 el = this.el.child('.roo-tpl-' + this.dataName);
20203 for(var i = 0, len = records.length; i < len; i++){
20204 var data = this.prepareData(records[i].data, i, records[i]);
20205 this.fireEvent("preparedata", this, data, i, records[i]);
20207 var d = Roo.apply({}, data);
20210 Roo.apply(d, {'roo-id' : Roo.id()});
20214 Roo.each(this.parent.item, function(item){
20215 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20218 Roo.apply(d, {'roo-data-checked' : 'checked'});
20222 html[html.length] = Roo.util.Format.trim(
20224 t.applySubtemplate(this.dataName, d, this.store.meta) :
20231 el.update(html.join(""));
20232 this.nodes = el.dom.childNodes;
20233 this.updateIndexes(0);
20238 * Function to override to reformat the data that is sent to
20239 * the template for each node.
20240 * DEPRICATED - use the preparedata event handler.
20241 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20242 * a JSON object for an UpdateManager bound view).
20244 prepareData : function(data, index, record)
20246 this.fireEvent("preparedata", this, data, index, record);
20250 onUpdate : function(ds, record){
20251 // Roo.log('on update');
20252 this.clearSelections();
20253 var index = this.store.indexOf(record);
20254 var n = this.nodes[index];
20255 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20256 n.parentNode.removeChild(n);
20257 this.updateIndexes(index, index);
20263 onAdd : function(ds, records, index)
20265 //Roo.log(['on Add', ds, records, index] );
20266 this.clearSelections();
20267 if(this.nodes.length == 0){
20271 var n = this.nodes[index];
20272 for(var i = 0, len = records.length; i < len; i++){
20273 var d = this.prepareData(records[i].data, i, records[i]);
20275 this.tpl.insertBefore(n, d);
20278 this.tpl.append(this.el, d);
20281 this.updateIndexes(index);
20284 onRemove : function(ds, record, index){
20285 // Roo.log('onRemove');
20286 this.clearSelections();
20287 var el = this.dataName ?
20288 this.el.child('.roo-tpl-' + this.dataName) :
20291 el.dom.removeChild(this.nodes[index]);
20292 this.updateIndexes(index);
20296 * Refresh an individual node.
20297 * @param {Number} index
20299 refreshNode : function(index){
20300 this.onUpdate(this.store, this.store.getAt(index));
20303 updateIndexes : function(startIndex, endIndex){
20304 var ns = this.nodes;
20305 startIndex = startIndex || 0;
20306 endIndex = endIndex || ns.length - 1;
20307 for(var i = startIndex; i <= endIndex; i++){
20308 ns[i].nodeIndex = i;
20313 * Changes the data store this view uses and refresh the view.
20314 * @param {Store} store
20316 setStore : function(store, initial){
20317 if(!initial && this.store){
20318 this.store.un("datachanged", this.refresh);
20319 this.store.un("add", this.onAdd);
20320 this.store.un("remove", this.onRemove);
20321 this.store.un("update", this.onUpdate);
20322 this.store.un("clear", this.refresh);
20323 this.store.un("beforeload", this.onBeforeLoad);
20324 this.store.un("load", this.onLoad);
20325 this.store.un("loadexception", this.onLoad);
20329 store.on("datachanged", this.refresh, this);
20330 store.on("add", this.onAdd, this);
20331 store.on("remove", this.onRemove, this);
20332 store.on("update", this.onUpdate, this);
20333 store.on("clear", this.refresh, this);
20334 store.on("beforeload", this.onBeforeLoad, this);
20335 store.on("load", this.onLoad, this);
20336 store.on("loadexception", this.onLoad, this);
20344 * onbeforeLoad - masks the loading area.
20347 onBeforeLoad : function(store,opts)
20349 //Roo.log('onBeforeLoad');
20351 this.el.update("");
20353 this.el.mask(this.mask ? this.mask : "Loading" );
20355 onLoad : function ()
20362 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20363 * @param {HTMLElement} node
20364 * @return {HTMLElement} The template node
20366 findItemFromChild : function(node){
20367 var el = this.dataName ?
20368 this.el.child('.roo-tpl-' + this.dataName,true) :
20371 if(!node || node.parentNode == el){
20374 var p = node.parentNode;
20375 while(p && p != el){
20376 if(p.parentNode == el){
20385 onClick : function(e){
20386 var item = this.findItemFromChild(e.getTarget());
20388 var index = this.indexOf(item);
20389 if(this.onItemClick(item, index, e) !== false){
20390 this.fireEvent("click", this, index, item, e);
20393 this.clearSelections();
20398 onContextMenu : function(e){
20399 var item = this.findItemFromChild(e.getTarget());
20401 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20406 onDblClick : function(e){
20407 var item = this.findItemFromChild(e.getTarget());
20409 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20413 onItemClick : function(item, index, e)
20415 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20418 if (this.toggleSelect) {
20419 var m = this.isSelected(item) ? 'unselect' : 'select';
20422 _t[m](item, true, false);
20425 if(this.multiSelect || this.singleSelect){
20426 if(this.multiSelect && e.shiftKey && this.lastSelection){
20427 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20429 this.select(item, this.multiSelect && e.ctrlKey);
20430 this.lastSelection = item;
20433 if(!this.tickable){
20434 e.preventDefault();
20442 * Get the number of selected nodes.
20445 getSelectionCount : function(){
20446 return this.selections.length;
20450 * Get the currently selected nodes.
20451 * @return {Array} An array of HTMLElements
20453 getSelectedNodes : function(){
20454 return this.selections;
20458 * Get the indexes of the selected nodes.
20461 getSelectedIndexes : function(){
20462 var indexes = [], s = this.selections;
20463 for(var i = 0, len = s.length; i < len; i++){
20464 indexes.push(s[i].nodeIndex);
20470 * Clear all selections
20471 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20473 clearSelections : function(suppressEvent){
20474 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20475 this.cmp.elements = this.selections;
20476 this.cmp.removeClass(this.selectedClass);
20477 this.selections = [];
20478 if(!suppressEvent){
20479 this.fireEvent("selectionchange", this, this.selections);
20485 * Returns true if the passed node is selected
20486 * @param {HTMLElement/Number} node The node or node index
20487 * @return {Boolean}
20489 isSelected : function(node){
20490 var s = this.selections;
20494 node = this.getNode(node);
20495 return s.indexOf(node) !== -1;
20500 * @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
20501 * @param {Boolean} keepExisting (optional) true to keep existing selections
20502 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20504 select : function(nodeInfo, keepExisting, suppressEvent){
20505 if(nodeInfo instanceof Array){
20507 this.clearSelections(true);
20509 for(var i = 0, len = nodeInfo.length; i < len; i++){
20510 this.select(nodeInfo[i], true, true);
20514 var node = this.getNode(nodeInfo);
20515 if(!node || this.isSelected(node)){
20516 return; // already selected.
20519 this.clearSelections(true);
20522 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20523 Roo.fly(node).addClass(this.selectedClass);
20524 this.selections.push(node);
20525 if(!suppressEvent){
20526 this.fireEvent("selectionchange", this, this.selections);
20534 * @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
20535 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20536 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20538 unselect : function(nodeInfo, keepExisting, suppressEvent)
20540 if(nodeInfo instanceof Array){
20541 Roo.each(this.selections, function(s) {
20542 this.unselect(s, nodeInfo);
20546 var node = this.getNode(nodeInfo);
20547 if(!node || !this.isSelected(node)){
20548 //Roo.log("not selected");
20549 return; // not selected.
20553 Roo.each(this.selections, function(s) {
20555 Roo.fly(node).removeClass(this.selectedClass);
20562 this.selections= ns;
20563 this.fireEvent("selectionchange", this, this.selections);
20567 * Gets a template node.
20568 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20569 * @return {HTMLElement} The node or null if it wasn't found
20571 getNode : function(nodeInfo){
20572 if(typeof nodeInfo == "string"){
20573 return document.getElementById(nodeInfo);
20574 }else if(typeof nodeInfo == "number"){
20575 return this.nodes[nodeInfo];
20581 * Gets a range template nodes.
20582 * @param {Number} startIndex
20583 * @param {Number} endIndex
20584 * @return {Array} An array of nodes
20586 getNodes : function(start, end){
20587 var ns = this.nodes;
20588 start = start || 0;
20589 end = typeof end == "undefined" ? ns.length - 1 : end;
20592 for(var i = start; i <= end; i++){
20596 for(var i = start; i >= end; i--){
20604 * Finds the index of the passed node
20605 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20606 * @return {Number} The index of the node or -1
20608 indexOf : function(node){
20609 node = this.getNode(node);
20610 if(typeof node.nodeIndex == "number"){
20611 return node.nodeIndex;
20613 var ns = this.nodes;
20614 for(var i = 0, len = ns.length; i < len; i++){
20625 * based on jquery fullcalendar
20629 Roo.bootstrap = Roo.bootstrap || {};
20631 * @class Roo.bootstrap.Calendar
20632 * @extends Roo.bootstrap.Component
20633 * Bootstrap Calendar class
20634 * @cfg {Boolean} loadMask (true|false) default false
20635 * @cfg {Object} header generate the user specific header of the calendar, default false
20638 * Create a new Container
20639 * @param {Object} config The config object
20644 Roo.bootstrap.Calendar = function(config){
20645 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20649 * Fires when a date is selected
20650 * @param {DatePicker} this
20651 * @param {Date} date The selected date
20655 * @event monthchange
20656 * Fires when the displayed month changes
20657 * @param {DatePicker} this
20658 * @param {Date} date The selected month
20660 'monthchange': true,
20662 * @event evententer
20663 * Fires when mouse over an event
20664 * @param {Calendar} this
20665 * @param {event} Event
20667 'evententer': true,
20669 * @event eventleave
20670 * Fires when the mouse leaves an
20671 * @param {Calendar} this
20674 'eventleave': true,
20676 * @event eventclick
20677 * Fires when the mouse click an
20678 * @param {Calendar} this
20687 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20690 * @cfg {Roo.data.Store} store
20691 * The data source for the calendar
20695 * @cfg {Number} startDay
20696 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20704 getAutoCreate : function(){
20707 var fc_button = function(name, corner, style, content ) {
20708 return Roo.apply({},{
20710 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20712 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20715 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20726 style : 'width:100%',
20733 cls : 'fc-header-left',
20735 fc_button('prev', 'left', 'arrow', '‹' ),
20736 fc_button('next', 'right', 'arrow', '›' ),
20737 { tag: 'span', cls: 'fc-header-space' },
20738 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20746 cls : 'fc-header-center',
20750 cls: 'fc-header-title',
20753 html : 'month / year'
20761 cls : 'fc-header-right',
20763 /* fc_button('month', 'left', '', 'month' ),
20764 fc_button('week', '', '', 'week' ),
20765 fc_button('day', 'right', '', 'day' )
20777 header = this.header;
20780 var cal_heads = function() {
20782 // fixme - handle this.
20784 for (var i =0; i < Date.dayNames.length; i++) {
20785 var d = Date.dayNames[i];
20788 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20789 html : d.substring(0,3)
20793 ret[0].cls += ' fc-first';
20794 ret[6].cls += ' fc-last';
20797 var cal_cell = function(n) {
20800 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20805 cls: 'fc-day-number',
20809 cls: 'fc-day-content',
20813 style: 'position: relative;' // height: 17px;
20825 var cal_rows = function() {
20828 for (var r = 0; r < 6; r++) {
20835 for (var i =0; i < Date.dayNames.length; i++) {
20836 var d = Date.dayNames[i];
20837 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20840 row.cn[0].cls+=' fc-first';
20841 row.cn[0].cn[0].style = 'min-height:90px';
20842 row.cn[6].cls+=' fc-last';
20846 ret[0].cls += ' fc-first';
20847 ret[4].cls += ' fc-prev-last';
20848 ret[5].cls += ' fc-last';
20855 cls: 'fc-border-separate',
20856 style : 'width:100%',
20864 cls : 'fc-first fc-last',
20882 cls : 'fc-content',
20883 style : "position: relative;",
20886 cls : 'fc-view fc-view-month fc-grid',
20887 style : 'position: relative',
20888 unselectable : 'on',
20891 cls : 'fc-event-container',
20892 style : 'position:absolute;z-index:8;top:0;left:0;'
20910 initEvents : function()
20913 throw "can not find store for calendar";
20919 style: "text-align:center",
20923 style: "background-color:white;width:50%;margin:250 auto",
20927 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20938 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20940 var size = this.el.select('.fc-content', true).first().getSize();
20941 this.maskEl.setSize(size.width, size.height);
20942 this.maskEl.enableDisplayMode("block");
20943 if(!this.loadMask){
20944 this.maskEl.hide();
20947 this.store = Roo.factory(this.store, Roo.data);
20948 this.store.on('load', this.onLoad, this);
20949 this.store.on('beforeload', this.onBeforeLoad, this);
20953 this.cells = this.el.select('.fc-day',true);
20954 //Roo.log(this.cells);
20955 this.textNodes = this.el.query('.fc-day-number');
20956 this.cells.addClassOnOver('fc-state-hover');
20958 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20959 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20960 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20961 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20963 this.on('monthchange', this.onMonthChange, this);
20965 this.update(new Date().clearTime());
20968 resize : function() {
20969 var sz = this.el.getSize();
20971 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20972 this.el.select('.fc-day-content div',true).setHeight(34);
20977 showPrevMonth : function(e){
20978 this.update(this.activeDate.add("mo", -1));
20980 showToday : function(e){
20981 this.update(new Date().clearTime());
20984 showNextMonth : function(e){
20985 this.update(this.activeDate.add("mo", 1));
20989 showPrevYear : function(){
20990 this.update(this.activeDate.add("y", -1));
20994 showNextYear : function(){
20995 this.update(this.activeDate.add("y", 1));
21000 update : function(date)
21002 var vd = this.activeDate;
21003 this.activeDate = date;
21004 // if(vd && this.el){
21005 // var t = date.getTime();
21006 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21007 // Roo.log('using add remove');
21009 // this.fireEvent('monthchange', this, date);
21011 // this.cells.removeClass("fc-state-highlight");
21012 // this.cells.each(function(c){
21013 // if(c.dateValue == t){
21014 // c.addClass("fc-state-highlight");
21015 // setTimeout(function(){
21016 // try{c.dom.firstChild.focus();}catch(e){}
21026 var days = date.getDaysInMonth();
21028 var firstOfMonth = date.getFirstDateOfMonth();
21029 var startingPos = firstOfMonth.getDay()-this.startDay;
21031 if(startingPos < this.startDay){
21035 var pm = date.add(Date.MONTH, -1);
21036 var prevStart = pm.getDaysInMonth()-startingPos;
21038 this.cells = this.el.select('.fc-day',true);
21039 this.textNodes = this.el.query('.fc-day-number');
21040 this.cells.addClassOnOver('fc-state-hover');
21042 var cells = this.cells.elements;
21043 var textEls = this.textNodes;
21045 Roo.each(cells, function(cell){
21046 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21049 days += startingPos;
21051 // convert everything to numbers so it's fast
21052 var day = 86400000;
21053 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21056 //Roo.log(prevStart);
21058 var today = new Date().clearTime().getTime();
21059 var sel = date.clearTime().getTime();
21060 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21061 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21062 var ddMatch = this.disabledDatesRE;
21063 var ddText = this.disabledDatesText;
21064 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21065 var ddaysText = this.disabledDaysText;
21066 var format = this.format;
21068 var setCellClass = function(cal, cell){
21072 //Roo.log('set Cell Class');
21074 var t = d.getTime();
21078 cell.dateValue = t;
21080 cell.className += " fc-today";
21081 cell.className += " fc-state-highlight";
21082 cell.title = cal.todayText;
21085 // disable highlight in other month..
21086 //cell.className += " fc-state-highlight";
21091 cell.className = " fc-state-disabled";
21092 cell.title = cal.minText;
21096 cell.className = " fc-state-disabled";
21097 cell.title = cal.maxText;
21101 if(ddays.indexOf(d.getDay()) != -1){
21102 cell.title = ddaysText;
21103 cell.className = " fc-state-disabled";
21106 if(ddMatch && format){
21107 var fvalue = d.dateFormat(format);
21108 if(ddMatch.test(fvalue)){
21109 cell.title = ddText.replace("%0", fvalue);
21110 cell.className = " fc-state-disabled";
21114 if (!cell.initialClassName) {
21115 cell.initialClassName = cell.dom.className;
21118 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21123 for(; i < startingPos; i++) {
21124 textEls[i].innerHTML = (++prevStart);
21125 d.setDate(d.getDate()+1);
21127 cells[i].className = "fc-past fc-other-month";
21128 setCellClass(this, cells[i]);
21133 for(; i < days; i++){
21134 intDay = i - startingPos + 1;
21135 textEls[i].innerHTML = (intDay);
21136 d.setDate(d.getDate()+1);
21138 cells[i].className = ''; // "x-date-active";
21139 setCellClass(this, cells[i]);
21143 for(; i < 42; i++) {
21144 textEls[i].innerHTML = (++extraDays);
21145 d.setDate(d.getDate()+1);
21147 cells[i].className = "fc-future fc-other-month";
21148 setCellClass(this, cells[i]);
21151 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21153 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21155 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21156 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21158 if(totalRows != 6){
21159 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21160 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21163 this.fireEvent('monthchange', this, date);
21167 if(!this.internalRender){
21168 var main = this.el.dom.firstChild;
21169 var w = main.offsetWidth;
21170 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21171 Roo.fly(main).setWidth(w);
21172 this.internalRender = true;
21173 // opera does not respect the auto grow header center column
21174 // then, after it gets a width opera refuses to recalculate
21175 // without a second pass
21176 if(Roo.isOpera && !this.secondPass){
21177 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21178 this.secondPass = true;
21179 this.update.defer(10, this, [date]);
21186 findCell : function(dt) {
21187 dt = dt.clearTime().getTime();
21189 this.cells.each(function(c){
21190 //Roo.log("check " +c.dateValue + '?=' + dt);
21191 if(c.dateValue == dt){
21201 findCells : function(ev) {
21202 var s = ev.start.clone().clearTime().getTime();
21204 var e= ev.end.clone().clearTime().getTime();
21207 this.cells.each(function(c){
21208 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21210 if(c.dateValue > e){
21213 if(c.dateValue < s){
21222 // findBestRow: function(cells)
21226 // for (var i =0 ; i < cells.length;i++) {
21227 // ret = Math.max(cells[i].rows || 0,ret);
21234 addItem : function(ev)
21236 // look for vertical location slot in
21237 var cells = this.findCells(ev);
21239 // ev.row = this.findBestRow(cells);
21241 // work out the location.
21245 for(var i =0; i < cells.length; i++) {
21247 cells[i].row = cells[0].row;
21250 cells[i].row = cells[i].row + 1;
21260 if (crow.start.getY() == cells[i].getY()) {
21262 crow.end = cells[i];
21279 cells[0].events.push(ev);
21281 this.calevents.push(ev);
21284 clearEvents: function() {
21286 if(!this.calevents){
21290 Roo.each(this.cells.elements, function(c){
21296 Roo.each(this.calevents, function(e) {
21297 Roo.each(e.els, function(el) {
21298 el.un('mouseenter' ,this.onEventEnter, this);
21299 el.un('mouseleave' ,this.onEventLeave, this);
21304 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21310 renderEvents: function()
21314 this.cells.each(function(c) {
21323 if(c.row != c.events.length){
21324 r = 4 - (4 - (c.row - c.events.length));
21327 c.events = ev.slice(0, r);
21328 c.more = ev.slice(r);
21330 if(c.more.length && c.more.length == 1){
21331 c.events.push(c.more.pop());
21334 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21338 this.cells.each(function(c) {
21340 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21343 for (var e = 0; e < c.events.length; e++){
21344 var ev = c.events[e];
21345 var rows = ev.rows;
21347 for(var i = 0; i < rows.length; i++) {
21349 // how many rows should it span..
21352 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21353 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21355 unselectable : "on",
21358 cls: 'fc-event-inner',
21362 // cls: 'fc-event-time',
21363 // html : cells.length > 1 ? '' : ev.time
21367 cls: 'fc-event-title',
21368 html : String.format('{0}', ev.title)
21375 cls: 'ui-resizable-handle ui-resizable-e',
21376 html : '  '
21383 cfg.cls += ' fc-event-start';
21385 if ((i+1) == rows.length) {
21386 cfg.cls += ' fc-event-end';
21389 var ctr = _this.el.select('.fc-event-container',true).first();
21390 var cg = ctr.createChild(cfg);
21392 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21393 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21395 var r = (c.more.length) ? 1 : 0;
21396 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21397 cg.setWidth(ebox.right - sbox.x -2);
21399 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21400 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21401 cg.on('click', _this.onEventClick, _this, ev);
21412 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21413 style : 'position: absolute',
21414 unselectable : "on",
21417 cls: 'fc-event-inner',
21421 cls: 'fc-event-title',
21429 cls: 'ui-resizable-handle ui-resizable-e',
21430 html : '  '
21436 var ctr = _this.el.select('.fc-event-container',true).first();
21437 var cg = ctr.createChild(cfg);
21439 var sbox = c.select('.fc-day-content',true).first().getBox();
21440 var ebox = c.select('.fc-day-content',true).first().getBox();
21442 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21443 cg.setWidth(ebox.right - sbox.x -2);
21445 cg.on('click', _this.onMoreEventClick, _this, c.more);
21455 onEventEnter: function (e, el,event,d) {
21456 this.fireEvent('evententer', this, el, event);
21459 onEventLeave: function (e, el,event,d) {
21460 this.fireEvent('eventleave', this, el, event);
21463 onEventClick: function (e, el,event,d) {
21464 this.fireEvent('eventclick', this, el, event);
21467 onMonthChange: function () {
21471 onMoreEventClick: function(e, el, more)
21475 this.calpopover.placement = 'right';
21476 this.calpopover.setTitle('More');
21478 this.calpopover.setContent('');
21480 var ctr = this.calpopover.el.select('.popover-content', true).first();
21482 Roo.each(more, function(m){
21484 cls : 'fc-event-hori fc-event-draggable',
21487 var cg = ctr.createChild(cfg);
21489 cg.on('click', _this.onEventClick, _this, m);
21492 this.calpopover.show(el);
21497 onLoad: function ()
21499 this.calevents = [];
21502 if(this.store.getCount() > 0){
21503 this.store.data.each(function(d){
21506 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21507 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21508 time : d.data.start_time,
21509 title : d.data.title,
21510 description : d.data.description,
21511 venue : d.data.venue
21516 this.renderEvents();
21518 if(this.calevents.length && this.loadMask){
21519 this.maskEl.hide();
21523 onBeforeLoad: function()
21525 this.clearEvents();
21527 this.maskEl.show();
21541 * @class Roo.bootstrap.Popover
21542 * @extends Roo.bootstrap.Component
21543 * @parent none builder
21544 * @children Roo.bootstrap.Component
21545 * Bootstrap Popover class
21546 * @cfg {String} html contents of the popover (or false to use children..)
21547 * @cfg {String} title of popover (or false to hide)
21548 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21549 * @cfg {String} trigger click || hover (or false to trigger manually)
21550 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21551 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21552 * - if false and it has a 'parent' then it will be automatically added to that element
21553 * - if string - Roo.get will be called
21554 * @cfg {Number} delay - delay before showing
21557 * Create a new Popover
21558 * @param {Object} config The config object
21561 Roo.bootstrap.Popover = function(config){
21562 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21568 * After the popover show
21570 * @param {Roo.bootstrap.Popover} this
21575 * After the popover hide
21577 * @param {Roo.bootstrap.Popover} this
21583 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21588 placement : 'right',
21589 trigger : 'hover', // hover
21595 can_build_overlaid : false,
21597 maskEl : false, // the mask element
21600 alignEl : false, // when show is called with an element - this get's stored.
21602 getChildContainer : function()
21604 return this.contentEl;
21607 getPopoverHeader : function()
21609 this.title = true; // flag not to hide it..
21610 this.headerEl.addClass('p-0');
21611 return this.headerEl
21615 getAutoCreate : function(){
21618 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21619 style: 'display:block',
21625 cls : 'popover-inner ',
21629 cls: 'popover-title popover-header',
21630 html : this.title === false ? '' : this.title
21633 cls : 'popover-content popover-body ' + (this.cls || ''),
21634 html : this.html || ''
21645 * @param {string} the title
21647 setTitle: function(str)
21651 this.headerEl.dom.innerHTML = str;
21656 * @param {string} the body content
21658 setContent: function(str)
21661 if (this.contentEl) {
21662 this.contentEl.dom.innerHTML = str;
21666 // as it get's added to the bottom of the page.
21667 onRender : function(ct, position)
21669 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21674 var cfg = Roo.apply({}, this.getAutoCreate());
21678 cfg.cls += ' ' + this.cls;
21681 cfg.style = this.style;
21683 //Roo.log("adding to ");
21684 this.el = Roo.get(document.body).createChild(cfg, position);
21685 // Roo.log(this.el);
21688 this.contentEl = this.el.select('.popover-content',true).first();
21689 this.headerEl = this.el.select('.popover-title',true).first();
21692 if(typeof(this.items) != 'undefined'){
21693 var items = this.items;
21696 for(var i =0;i < items.length;i++) {
21697 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21701 this.items = nitems;
21703 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21704 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21711 resizeMask : function()
21713 this.maskEl.setSize(
21714 Roo.lib.Dom.getViewWidth(true),
21715 Roo.lib.Dom.getViewHeight(true)
21719 initEvents : function()
21723 Roo.bootstrap.Popover.register(this);
21726 this.arrowEl = this.el.select('.arrow',true).first();
21727 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21728 this.el.enableDisplayMode('block');
21732 if (this.over === false && !this.parent()) {
21735 if (this.triggers === false) {
21740 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21741 var triggers = this.trigger ? this.trigger.split(' ') : [];
21742 Roo.each(triggers, function(trigger) {
21744 if (trigger == 'click') {
21745 on_el.on('click', this.toggle, this);
21746 } else if (trigger != 'manual') {
21747 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21748 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21750 on_el.on(eventIn ,this.enter, this);
21751 on_el.on(eventOut, this.leave, this);
21761 toggle : function () {
21762 this.hoverState == 'in' ? this.leave() : this.enter();
21765 enter : function () {
21767 clearTimeout(this.timeout);
21769 this.hoverState = 'in';
21771 if (!this.delay || !this.delay.show) {
21776 this.timeout = setTimeout(function () {
21777 if (_t.hoverState == 'in') {
21780 }, this.delay.show)
21783 leave : function() {
21784 clearTimeout(this.timeout);
21786 this.hoverState = 'out';
21788 if (!this.delay || !this.delay.hide) {
21793 this.timeout = setTimeout(function () {
21794 if (_t.hoverState == 'out') {
21797 }, this.delay.hide)
21801 * update the position of the dialog
21802 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21807 doAlign : function()
21810 if (this.alignEl) {
21811 this.updatePosition(this.placement, true);
21814 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21815 var es = this.el.getSize();
21816 var x = Roo.lib.Dom.getViewWidth()/2;
21817 var y = Roo.lib.Dom.getViewHeight()/2;
21818 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21830 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21831 * @param {string} (left|right|top|bottom) position
21833 show : function (on_el, placement)
21835 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21836 on_el = on_el || false; // default to false
21839 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21840 on_el = this.parent().el;
21841 } else if (this.over) {
21842 on_el = Roo.get(this.over);
21847 this.alignEl = Roo.get( on_el );
21850 this.render(document.body);
21856 if (this.title === false) {
21857 this.headerEl.hide();
21862 this.el.dom.style.display = 'block';
21866 //var arrow = this.el.select('.arrow',true).first();
21867 //arrow.set(align[2],
21869 this.el.addClass('in');
21873 this.hoverState = 'in';
21876 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21877 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21878 this.maskEl.dom.style.display = 'block';
21879 this.maskEl.addClass('show');
21881 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21883 this.fireEvent('show', this);
21887 * fire this manually after loading a grid in the table for example
21888 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21889 * @param {Boolean} try and move it if we cant get right position.
21891 updatePosition : function(placement, try_move)
21893 // allow for calling with no parameters
21894 placement = placement ? placement : this.placement;
21895 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21897 this.el.removeClass([
21898 'fade','top','bottom', 'left', 'right','in',
21899 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21901 this.el.addClass(placement + ' bs-popover-' + placement);
21903 if (!this.alignEl ) {
21907 switch (placement) {
21909 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21910 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21911 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21912 //normal display... or moved up/down.
21913 this.el.setXY(offset);
21914 var xy = this.alignEl.getAnchorXY('tr', false);
21916 this.arrowEl.setXY(xy);
21919 // continue through...
21920 return this.updatePosition('left', false);
21924 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21925 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21926 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21927 //normal display... or moved up/down.
21928 this.el.setXY(offset);
21929 var xy = this.alignEl.getAnchorXY('tl', false);
21930 xy[0]-=10;xy[1]+=5; // << fix me
21931 this.arrowEl.setXY(xy);
21935 return this.updatePosition('right', false);
21938 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21939 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21940 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21941 //normal display... or moved up/down.
21942 this.el.setXY(offset);
21943 var xy = this.alignEl.getAnchorXY('t', false);
21944 xy[1]-=10; // << fix me
21945 this.arrowEl.setXY(xy);
21949 return this.updatePosition('bottom', false);
21952 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21953 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21954 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21955 //normal display... or moved up/down.
21956 this.el.setXY(offset);
21957 var xy = this.alignEl.getAnchorXY('b', false);
21958 xy[1]+=2; // << fix me
21959 this.arrowEl.setXY(xy);
21963 return this.updatePosition('top', false);
21974 this.el.setXY([0,0]);
21975 this.el.removeClass('in');
21977 this.hoverState = null;
21978 this.maskEl.hide(); // always..
21979 this.fireEvent('hide', this);
21985 Roo.apply(Roo.bootstrap.Popover, {
21988 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21989 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21990 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21991 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21996 clickHander : false,
22000 onMouseDown : function(e)
22002 if (this.popups.length && !e.getTarget(".roo-popover")) {
22003 /// what is nothing is showing..
22012 register : function(popup)
22014 if (!Roo.bootstrap.Popover.clickHandler) {
22015 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22017 // hide other popups.
22018 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22019 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22020 this.hideAll(); //<< why?
22021 //this.popups.push(popup);
22023 hideAll : function()
22025 this.popups.forEach(function(p) {
22029 onShow : function() {
22030 Roo.bootstrap.Popover.popups.push(this);
22032 onHide : function() {
22033 Roo.bootstrap.Popover.popups.remove(this);
22038 * @class Roo.bootstrap.PopoverNav
22039 * @extends Roo.bootstrap.nav.Simplebar
22040 * @parent Roo.bootstrap.Popover
22041 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22043 * Bootstrap Popover header navigation class
22044 * FIXME? should this go under nav?
22048 * Create a new Popover Header Navigation
22049 * @param {Object} config The config object
22052 Roo.bootstrap.PopoverNav = function(config){
22053 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22056 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22059 container_method : 'getPopoverHeader'
22077 * @class Roo.bootstrap.Progress
22078 * @extends Roo.bootstrap.Component
22079 * @children Roo.bootstrap.ProgressBar
22080 * Bootstrap Progress class
22081 * @cfg {Boolean} striped striped of the progress bar
22082 * @cfg {Boolean} active animated of the progress bar
22086 * Create a new Progress
22087 * @param {Object} config The config object
22090 Roo.bootstrap.Progress = function(config){
22091 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22094 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22099 getAutoCreate : function(){
22107 cfg.cls += ' progress-striped';
22111 cfg.cls += ' active';
22130 * @class Roo.bootstrap.ProgressBar
22131 * @extends Roo.bootstrap.Component
22132 * Bootstrap ProgressBar class
22133 * @cfg {Number} aria_valuenow aria-value now
22134 * @cfg {Number} aria_valuemin aria-value min
22135 * @cfg {Number} aria_valuemax aria-value max
22136 * @cfg {String} label label for the progress bar
22137 * @cfg {String} panel (success | info | warning | danger )
22138 * @cfg {String} role role of the progress bar
22139 * @cfg {String} sr_only text
22143 * Create a new ProgressBar
22144 * @param {Object} config The config object
22147 Roo.bootstrap.ProgressBar = function(config){
22148 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22151 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22155 aria_valuemax : 100,
22161 getAutoCreate : function()
22166 cls: 'progress-bar',
22167 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22179 cfg.role = this.role;
22182 if(this.aria_valuenow){
22183 cfg['aria-valuenow'] = this.aria_valuenow;
22186 if(this.aria_valuemin){
22187 cfg['aria-valuemin'] = this.aria_valuemin;
22190 if(this.aria_valuemax){
22191 cfg['aria-valuemax'] = this.aria_valuemax;
22194 if(this.label && !this.sr_only){
22195 cfg.html = this.label;
22199 cfg.cls += ' progress-bar-' + this.panel;
22205 update : function(aria_valuenow)
22207 this.aria_valuenow = aria_valuenow;
22209 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22217 * @class Roo.bootstrap.TabGroup
22218 * @extends Roo.bootstrap.Column
22219 * @children Roo.bootstrap.TabPanel
22220 * Bootstrap Column class
22221 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22222 * @cfg {Boolean} carousel true to make the group behave like a carousel
22223 * @cfg {Boolean} bullets show bullets for the panels
22224 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22225 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22226 * @cfg {Boolean} showarrow (true|false) show arrow default true
22229 * Create a new TabGroup
22230 * @param {Object} config The config object
22233 Roo.bootstrap.TabGroup = function(config){
22234 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22236 this.navId = Roo.id();
22239 Roo.bootstrap.TabGroup.register(this);
22243 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22246 transition : false,
22251 slideOnTouch : false,
22254 getAutoCreate : function()
22256 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22258 cfg.cls += ' tab-content';
22260 if (this.carousel) {
22261 cfg.cls += ' carousel slide';
22264 cls : 'carousel-inner',
22268 if(this.bullets && !Roo.isTouch){
22271 cls : 'carousel-bullets',
22275 if(this.bullets_cls){
22276 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22283 cfg.cn[0].cn.push(bullets);
22286 if(this.showarrow){
22287 cfg.cn[0].cn.push({
22289 class : 'carousel-arrow',
22293 class : 'carousel-prev',
22297 class : 'fa fa-chevron-left'
22303 class : 'carousel-next',
22307 class : 'fa fa-chevron-right'
22320 initEvents: function()
22322 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22323 // this.el.on("touchstart", this.onTouchStart, this);
22326 if(this.autoslide){
22329 this.slideFn = window.setInterval(function() {
22330 _this.showPanelNext();
22334 if(this.showarrow){
22335 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22336 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22342 // onTouchStart : function(e, el, o)
22344 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22348 // this.showPanelNext();
22352 getChildContainer : function()
22354 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22358 * register a Navigation item
22359 * @param {Roo.bootstrap.nav.Item} the navitem to add
22361 register : function(item)
22363 this.tabs.push( item);
22364 item.navId = this.navId; // not really needed..
22369 getActivePanel : function()
22372 Roo.each(this.tabs, function(t) {
22382 getPanelByName : function(n)
22385 Roo.each(this.tabs, function(t) {
22386 if (t.tabId == n) {
22394 indexOfPanel : function(p)
22397 Roo.each(this.tabs, function(t,i) {
22398 if (t.tabId == p.tabId) {
22407 * show a specific panel
22408 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22409 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22411 showPanel : function (pan)
22413 if(this.transition || typeof(pan) == 'undefined'){
22414 Roo.log("waiting for the transitionend");
22418 if (typeof(pan) == 'number') {
22419 pan = this.tabs[pan];
22422 if (typeof(pan) == 'string') {
22423 pan = this.getPanelByName(pan);
22426 var cur = this.getActivePanel();
22429 Roo.log('pan or acitve pan is undefined');
22433 if (pan.tabId == this.getActivePanel().tabId) {
22437 if (false === cur.fireEvent('beforedeactivate')) {
22441 if(this.bullets > 0 && !Roo.isTouch){
22442 this.setActiveBullet(this.indexOfPanel(pan));
22445 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22447 //class="carousel-item carousel-item-next carousel-item-left"
22449 this.transition = true;
22450 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22451 var lr = dir == 'next' ? 'left' : 'right';
22452 pan.el.addClass(dir); // or prev
22453 pan.el.addClass('carousel-item-' + dir); // or prev
22454 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22455 cur.el.addClass(lr); // or right
22456 pan.el.addClass(lr);
22457 cur.el.addClass('carousel-item-' +lr); // or right
22458 pan.el.addClass('carousel-item-' +lr);
22462 cur.el.on('transitionend', function() {
22463 Roo.log("trans end?");
22465 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22466 pan.setActive(true);
22468 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22469 cur.setActive(false);
22471 _this.transition = false;
22473 }, this, { single: true } );
22478 cur.setActive(false);
22479 pan.setActive(true);
22484 showPanelNext : function()
22486 var i = this.indexOfPanel(this.getActivePanel());
22488 if (i >= this.tabs.length - 1 && !this.autoslide) {
22492 if (i >= this.tabs.length - 1 && this.autoslide) {
22496 this.showPanel(this.tabs[i+1]);
22499 showPanelPrev : function()
22501 var i = this.indexOfPanel(this.getActivePanel());
22503 if (i < 1 && !this.autoslide) {
22507 if (i < 1 && this.autoslide) {
22508 i = this.tabs.length;
22511 this.showPanel(this.tabs[i-1]);
22515 addBullet: function()
22517 if(!this.bullets || Roo.isTouch){
22520 var ctr = this.el.select('.carousel-bullets',true).first();
22521 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22522 var bullet = ctr.createChild({
22523 cls : 'bullet bullet-' + i
22524 },ctr.dom.lastChild);
22529 bullet.on('click', (function(e, el, o, ii, t){
22531 e.preventDefault();
22533 this.showPanel(ii);
22535 if(this.autoslide && this.slideFn){
22536 clearInterval(this.slideFn);
22537 this.slideFn = window.setInterval(function() {
22538 _this.showPanelNext();
22542 }).createDelegate(this, [i, bullet], true));
22547 setActiveBullet : function(i)
22553 Roo.each(this.el.select('.bullet', true).elements, function(el){
22554 el.removeClass('selected');
22557 var bullet = this.el.select('.bullet-' + i, true).first();
22563 bullet.addClass('selected');
22574 Roo.apply(Roo.bootstrap.TabGroup, {
22578 * register a Navigation Group
22579 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22581 register : function(navgrp)
22583 this.groups[navgrp.navId] = navgrp;
22587 * fetch a Navigation Group based on the navigation ID
22588 * if one does not exist , it will get created.
22589 * @param {string} the navgroup to add
22590 * @returns {Roo.bootstrap.nav.Group} the navgroup
22592 get: function(navId) {
22593 if (typeof(this.groups[navId]) == 'undefined') {
22594 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22596 return this.groups[navId] ;
22611 * @class Roo.bootstrap.TabPanel
22612 * @extends Roo.bootstrap.Component
22613 * @children Roo.bootstrap.Component
22614 * Bootstrap TabPanel class
22615 * @cfg {Boolean} active panel active
22616 * @cfg {String} html panel content
22617 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22618 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22619 * @cfg {String} href click to link..
22620 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22624 * Create a new TabPanel
22625 * @param {Object} config The config object
22628 Roo.bootstrap.TabPanel = function(config){
22629 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22633 * Fires when the active status changes
22634 * @param {Roo.bootstrap.TabPanel} this
22635 * @param {Boolean} state the new state
22640 * @event beforedeactivate
22641 * Fires before a tab is de-activated - can be used to do validation on a form.
22642 * @param {Roo.bootstrap.TabPanel} this
22643 * @return {Boolean} false if there is an error
22646 'beforedeactivate': true
22649 this.tabId = this.tabId || Roo.id();
22653 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22660 touchSlide : false,
22661 getAutoCreate : function(){
22666 // item is needed for carousel - not sure if it has any effect otherwise
22667 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22668 html: this.html || ''
22672 cfg.cls += ' active';
22676 cfg.tabId = this.tabId;
22684 initEvents: function()
22686 var p = this.parent();
22688 this.navId = this.navId || p.navId;
22690 if (typeof(this.navId) != 'undefined') {
22691 // not really needed.. but just in case.. parent should be a NavGroup.
22692 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22696 var i = tg.tabs.length - 1;
22698 if(this.active && tg.bullets > 0 && i < tg.bullets){
22699 tg.setActiveBullet(i);
22703 this.el.on('click', this.onClick, this);
22705 if(Roo.isTouch && this.touchSlide){
22706 this.el.on("touchstart", this.onTouchStart, this);
22707 this.el.on("touchmove", this.onTouchMove, this);
22708 this.el.on("touchend", this.onTouchEnd, this);
22713 onRender : function(ct, position)
22715 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22718 setActive : function(state)
22720 Roo.log("panel - set active " + this.tabId + "=" + state);
22722 this.active = state;
22724 this.el.removeClass('active');
22726 } else if (!this.el.hasClass('active')) {
22727 this.el.addClass('active');
22730 this.fireEvent('changed', this, state);
22733 onClick : function(e)
22735 e.preventDefault();
22737 if(!this.href.length){
22741 window.location.href = this.href;
22750 onTouchStart : function(e)
22752 this.swiping = false;
22754 this.startX = e.browserEvent.touches[0].clientX;
22755 this.startY = e.browserEvent.touches[0].clientY;
22758 onTouchMove : function(e)
22760 this.swiping = true;
22762 this.endX = e.browserEvent.touches[0].clientX;
22763 this.endY = e.browserEvent.touches[0].clientY;
22766 onTouchEnd : function(e)
22773 var tabGroup = this.parent();
22775 if(this.endX > this.startX){ // swiping right
22776 tabGroup.showPanelPrev();
22780 if(this.startX > this.endX){ // swiping left
22781 tabGroup.showPanelNext();
22800 * @class Roo.bootstrap.form.DateField
22801 * @extends Roo.bootstrap.form.Input
22802 * Bootstrap DateField class
22803 * @cfg {Number} weekStart default 0
22804 * @cfg {String} viewMode default empty, (months|years)
22805 * @cfg {String} minViewMode default empty, (months|years)
22806 * @cfg {Number} startDate default -Infinity
22807 * @cfg {Number} endDate default Infinity
22808 * @cfg {Boolean} todayHighlight default false
22809 * @cfg {Boolean} todayBtn default false
22810 * @cfg {Boolean} calendarWeeks default false
22811 * @cfg {Object} daysOfWeekDisabled default empty
22812 * @cfg {Boolean} singleMode default false (true | false)
22814 * @cfg {Boolean} keyboardNavigation default true
22815 * @cfg {String} language default en
22818 * Create a new DateField
22819 * @param {Object} config The config object
22822 Roo.bootstrap.form.DateField = function(config){
22823 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22827 * Fires when this field show.
22828 * @param {Roo.bootstrap.form.DateField} this
22829 * @param {Mixed} date The date value
22834 * Fires when this field hide.
22835 * @param {Roo.bootstrap.form.DateField} this
22836 * @param {Mixed} date The date value
22841 * Fires when select a date.
22842 * @param {Roo.bootstrap.form.DateField} this
22843 * @param {Mixed} date The date value
22847 * @event beforeselect
22848 * Fires when before select a date.
22849 * @param {Roo.bootstrap.form.DateField} this
22850 * @param {Mixed} date The date value
22852 beforeselect : true
22856 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22859 * @cfg {String} format
22860 * The default date format string which can be overriden for localization support. The format must be
22861 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22865 * @cfg {String} altFormats
22866 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22867 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22869 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22877 todayHighlight : false,
22883 keyboardNavigation: true,
22885 calendarWeeks: false,
22887 startDate: -Infinity,
22891 daysOfWeekDisabled: [],
22895 singleMode : false,
22897 UTCDate: function()
22899 return new Date(Date.UTC.apply(Date, arguments));
22902 UTCToday: function()
22904 var today = new Date();
22905 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22908 getDate: function() {
22909 var d = this.getUTCDate();
22910 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22913 getUTCDate: function() {
22917 setDate: function(d) {
22918 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22921 setUTCDate: function(d) {
22923 this.setValue(this.formatDate(this.date));
22926 onRender: function(ct, position)
22929 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22931 this.language = this.language || 'en';
22932 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22933 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22935 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22936 this.format = this.format || 'm/d/y';
22937 this.isInline = false;
22938 this.isInput = true;
22939 this.component = this.el.select('.add-on', true).first() || false;
22940 this.component = (this.component && this.component.length === 0) ? false : this.component;
22941 this.hasInput = this.component && this.inputEl().length;
22943 if (typeof(this.minViewMode === 'string')) {
22944 switch (this.minViewMode) {
22946 this.minViewMode = 1;
22949 this.minViewMode = 2;
22952 this.minViewMode = 0;
22957 if (typeof(this.viewMode === 'string')) {
22958 switch (this.viewMode) {
22971 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22973 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22975 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22977 this.picker().on('mousedown', this.onMousedown, this);
22978 this.picker().on('click', this.onClick, this);
22980 this.picker().addClass('datepicker-dropdown');
22982 this.startViewMode = this.viewMode;
22984 if(this.singleMode){
22985 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22986 v.setVisibilityMode(Roo.Element.DISPLAY);
22990 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22991 v.setStyle('width', '189px');
22995 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22996 if(!this.calendarWeeks){
23001 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23002 v.attr('colspan', function(i, val){
23003 return parseInt(val) + 1;
23008 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23010 this.setStartDate(this.startDate);
23011 this.setEndDate(this.endDate);
23013 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23020 if(this.isInline) {
23025 picker : function()
23027 return this.pickerEl;
23028 // return this.el.select('.datepicker', true).first();
23031 fillDow: function()
23033 var dowCnt = this.weekStart;
23042 if(this.calendarWeeks){
23050 while (dowCnt < this.weekStart + 7) {
23054 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23058 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23061 fillMonths: function()
23064 var months = this.picker().select('>.datepicker-months td', true).first();
23066 months.dom.innerHTML = '';
23072 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23075 months.createChild(month);
23082 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;
23084 if (this.date < this.startDate) {
23085 this.viewDate = new Date(this.startDate);
23086 } else if (this.date > this.endDate) {
23087 this.viewDate = new Date(this.endDate);
23089 this.viewDate = new Date(this.date);
23097 var d = new Date(this.viewDate),
23098 year = d.getUTCFullYear(),
23099 month = d.getUTCMonth(),
23100 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23101 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23102 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23103 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23104 currentDate = this.date && this.date.valueOf(),
23105 today = this.UTCToday();
23107 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23109 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23111 // this.picker.select('>tfoot th.today').
23112 // .text(dates[this.language].today)
23113 // .toggle(this.todayBtn !== false);
23115 this.updateNavArrows();
23118 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23120 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23122 prevMonth.setUTCDate(day);
23124 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23126 var nextMonth = new Date(prevMonth);
23128 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23130 nextMonth = nextMonth.valueOf();
23132 var fillMonths = false;
23134 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23136 while(prevMonth.valueOf() <= nextMonth) {
23139 if (prevMonth.getUTCDay() === this.weekStart) {
23141 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23149 if(this.calendarWeeks){
23150 // ISO 8601: First week contains first thursday.
23151 // ISO also states week starts on Monday, but we can be more abstract here.
23153 // Start of current week: based on weekstart/current date
23154 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23155 // Thursday of this week
23156 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23157 // First Thursday of year, year from thursday
23158 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23159 // Calendar week: ms between thursdays, div ms per day, div 7 days
23160 calWeek = (th - yth) / 864e5 / 7 + 1;
23162 fillMonths.cn.push({
23170 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23172 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23175 if (this.todayHighlight &&
23176 prevMonth.getUTCFullYear() == today.getFullYear() &&
23177 prevMonth.getUTCMonth() == today.getMonth() &&
23178 prevMonth.getUTCDate() == today.getDate()) {
23179 clsName += ' today';
23182 if (currentDate && prevMonth.valueOf() === currentDate) {
23183 clsName += ' active';
23186 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23187 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23188 clsName += ' disabled';
23191 fillMonths.cn.push({
23193 cls: 'day ' + clsName,
23194 html: prevMonth.getDate()
23197 prevMonth.setDate(prevMonth.getDate()+1);
23200 var currentYear = this.date && this.date.getUTCFullYear();
23201 var currentMonth = this.date && this.date.getUTCMonth();
23203 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23205 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23206 v.removeClass('active');
23208 if(currentYear === year && k === currentMonth){
23209 v.addClass('active');
23212 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23213 v.addClass('disabled');
23219 year = parseInt(year/10, 10) * 10;
23221 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23223 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23226 for (var i = -1; i < 11; i++) {
23227 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23229 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23237 showMode: function(dir)
23240 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23243 Roo.each(this.picker().select('>div',true).elements, function(v){
23244 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23247 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23252 if(this.isInline) {
23256 this.picker().removeClass(['bottom', 'top']);
23258 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23260 * place to the top of element!
23264 this.picker().addClass('top');
23265 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23270 this.picker().addClass('bottom');
23272 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23275 parseDate : function(value)
23277 if(!value || value instanceof Date){
23280 var v = Date.parseDate(value, this.format);
23281 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23282 v = Date.parseDate(value, 'Y-m-d');
23284 if(!v && this.altFormats){
23285 if(!this.altFormatsArray){
23286 this.altFormatsArray = this.altFormats.split("|");
23288 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23289 v = Date.parseDate(value, this.altFormatsArray[i]);
23295 formatDate : function(date, fmt)
23297 return (!date || !(date instanceof Date)) ?
23298 date : date.dateFormat(fmt || this.format);
23301 onFocus : function()
23303 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23307 onBlur : function()
23309 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23311 var d = this.inputEl().getValue();
23318 showPopup : function()
23320 this.picker().show();
23324 this.fireEvent('showpopup', this, this.date);
23327 hidePopup : function()
23329 if(this.isInline) {
23332 this.picker().hide();
23333 this.viewMode = this.startViewMode;
23336 this.fireEvent('hidepopup', this, this.date);
23340 onMousedown: function(e)
23342 e.stopPropagation();
23343 e.preventDefault();
23348 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23352 setValue: function(v)
23354 if(this.fireEvent('beforeselect', this, v) !== false){
23355 var d = new Date(this.parseDate(v) ).clearTime();
23357 if(isNaN(d.getTime())){
23358 this.date = this.viewDate = '';
23359 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23363 v = this.formatDate(d);
23365 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23367 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23371 this.fireEvent('select', this, this.date);
23375 getValue: function()
23377 return this.formatDate(this.date);
23380 fireKey: function(e)
23382 if (!this.picker().isVisible()){
23383 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23389 var dateChanged = false,
23391 newDate, newViewDate;
23396 e.preventDefault();
23400 if (!this.keyboardNavigation) {
23403 dir = e.keyCode == 37 ? -1 : 1;
23406 newDate = this.moveYear(this.date, dir);
23407 newViewDate = this.moveYear(this.viewDate, dir);
23408 } else if (e.shiftKey){
23409 newDate = this.moveMonth(this.date, dir);
23410 newViewDate = this.moveMonth(this.viewDate, dir);
23412 newDate = new Date(this.date);
23413 newDate.setUTCDate(this.date.getUTCDate() + dir);
23414 newViewDate = new Date(this.viewDate);
23415 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23417 if (this.dateWithinRange(newDate)){
23418 this.date = newDate;
23419 this.viewDate = newViewDate;
23420 this.setValue(this.formatDate(this.date));
23422 e.preventDefault();
23423 dateChanged = true;
23428 if (!this.keyboardNavigation) {
23431 dir = e.keyCode == 38 ? -1 : 1;
23433 newDate = this.moveYear(this.date, dir);
23434 newViewDate = this.moveYear(this.viewDate, dir);
23435 } else if (e.shiftKey){
23436 newDate = this.moveMonth(this.date, dir);
23437 newViewDate = this.moveMonth(this.viewDate, dir);
23439 newDate = new Date(this.date);
23440 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23441 newViewDate = new Date(this.viewDate);
23442 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23444 if (this.dateWithinRange(newDate)){
23445 this.date = newDate;
23446 this.viewDate = newViewDate;
23447 this.setValue(this.formatDate(this.date));
23449 e.preventDefault();
23450 dateChanged = true;
23454 this.setValue(this.formatDate(this.date));
23456 e.preventDefault();
23459 this.setValue(this.formatDate(this.date));
23473 onClick: function(e)
23475 e.stopPropagation();
23476 e.preventDefault();
23478 var target = e.getTarget();
23480 if(target.nodeName.toLowerCase() === 'i'){
23481 target = Roo.get(target).dom.parentNode;
23484 var nodeName = target.nodeName;
23485 var className = target.className;
23486 var html = target.innerHTML;
23487 //Roo.log(nodeName);
23489 switch(nodeName.toLowerCase()) {
23491 switch(className) {
23497 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23498 switch(this.viewMode){
23500 this.viewDate = this.moveMonth(this.viewDate, dir);
23504 this.viewDate = this.moveYear(this.viewDate, dir);
23510 var date = new Date();
23511 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23513 this.setValue(this.formatDate(this.date));
23520 if (className.indexOf('disabled') < 0) {
23521 if (!this.viewDate) {
23522 this.viewDate = new Date();
23524 this.viewDate.setUTCDate(1);
23525 if (className.indexOf('month') > -1) {
23526 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23528 var year = parseInt(html, 10) || 0;
23529 this.viewDate.setUTCFullYear(year);
23533 if(this.singleMode){
23534 this.setValue(this.formatDate(this.viewDate));
23545 //Roo.log(className);
23546 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23547 var day = parseInt(html, 10) || 1;
23548 var year = (this.viewDate || new Date()).getUTCFullYear(),
23549 month = (this.viewDate || new Date()).getUTCMonth();
23551 if (className.indexOf('old') > -1) {
23558 } else if (className.indexOf('new') > -1) {
23566 //Roo.log([year,month,day]);
23567 this.date = this.UTCDate(year, month, day,0,0,0,0);
23568 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23570 //Roo.log(this.formatDate(this.date));
23571 this.setValue(this.formatDate(this.date));
23578 setStartDate: function(startDate)
23580 this.startDate = startDate || -Infinity;
23581 if (this.startDate !== -Infinity) {
23582 this.startDate = this.parseDate(this.startDate);
23585 this.updateNavArrows();
23588 setEndDate: function(endDate)
23590 this.endDate = endDate || Infinity;
23591 if (this.endDate !== Infinity) {
23592 this.endDate = this.parseDate(this.endDate);
23595 this.updateNavArrows();
23598 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23600 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23601 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23602 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23604 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23605 return parseInt(d, 10);
23608 this.updateNavArrows();
23611 updateNavArrows: function()
23613 if(this.singleMode){
23617 var d = new Date(this.viewDate),
23618 year = d.getUTCFullYear(),
23619 month = d.getUTCMonth();
23621 Roo.each(this.picker().select('.prev', true).elements, function(v){
23623 switch (this.viewMode) {
23626 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23632 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23639 Roo.each(this.picker().select('.next', true).elements, function(v){
23641 switch (this.viewMode) {
23644 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23650 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23658 moveMonth: function(date, dir)
23663 var new_date = new Date(date.valueOf()),
23664 day = new_date.getUTCDate(),
23665 month = new_date.getUTCMonth(),
23666 mag = Math.abs(dir),
23668 dir = dir > 0 ? 1 : -1;
23671 // If going back one month, make sure month is not current month
23672 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23674 return new_date.getUTCMonth() == month;
23676 // If going forward one month, make sure month is as expected
23677 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23679 return new_date.getUTCMonth() != new_month;
23681 new_month = month + dir;
23682 new_date.setUTCMonth(new_month);
23683 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23684 if (new_month < 0 || new_month > 11) {
23685 new_month = (new_month + 12) % 12;
23688 // For magnitudes >1, move one month at a time...
23689 for (var i=0; i<mag; i++) {
23690 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23691 new_date = this.moveMonth(new_date, dir);
23693 // ...then reset the day, keeping it in the new month
23694 new_month = new_date.getUTCMonth();
23695 new_date.setUTCDate(day);
23697 return new_month != new_date.getUTCMonth();
23700 // Common date-resetting loop -- if date is beyond end of month, make it
23703 new_date.setUTCDate(--day);
23704 new_date.setUTCMonth(new_month);
23709 moveYear: function(date, dir)
23711 return this.moveMonth(date, dir*12);
23714 dateWithinRange: function(date)
23716 return date >= this.startDate && date <= this.endDate;
23722 this.picker().remove();
23725 validateValue : function(value)
23727 if(this.getVisibilityEl().hasClass('hidden')){
23731 if(value.length < 1) {
23732 if(this.allowBlank){
23738 if(value.length < this.minLength){
23741 if(value.length > this.maxLength){
23745 var vt = Roo.form.VTypes;
23746 if(!vt[this.vtype](value, this)){
23750 if(typeof this.validator == "function"){
23751 var msg = this.validator(value);
23757 if(this.regex && !this.regex.test(value)){
23761 if(typeof(this.parseDate(value)) == 'undefined'){
23765 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23769 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23779 this.date = this.viewDate = '';
23781 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23786 Roo.apply(Roo.bootstrap.form.DateField, {
23797 html: '<i class="fa fa-arrow-left"/>'
23807 html: '<i class="fa fa-arrow-right"/>'
23849 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23850 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23851 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23852 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23853 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23866 navFnc: 'FullYear',
23871 navFnc: 'FullYear',
23876 Roo.apply(Roo.bootstrap.form.DateField, {
23880 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23884 cls: 'datepicker-days',
23888 cls: 'table-condensed',
23890 Roo.bootstrap.form.DateField.head,
23894 Roo.bootstrap.form.DateField.footer
23901 cls: 'datepicker-months',
23905 cls: 'table-condensed',
23907 Roo.bootstrap.form.DateField.head,
23908 Roo.bootstrap.form.DateField.content,
23909 Roo.bootstrap.form.DateField.footer
23916 cls: 'datepicker-years',
23920 cls: 'table-condensed',
23922 Roo.bootstrap.form.DateField.head,
23923 Roo.bootstrap.form.DateField.content,
23924 Roo.bootstrap.form.DateField.footer
23943 * @class Roo.bootstrap.form.TimeField
23944 * @extends Roo.bootstrap.form.Input
23945 * Bootstrap DateField class
23946 * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23950 * Create a new TimeField
23951 * @param {Object} config The config object
23954 Roo.bootstrap.form.TimeField = function(config){
23955 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23959 * Fires when this field show.
23960 * @param {Roo.bootstrap.form.DateField} thisthis
23961 * @param {Mixed} date The date value
23966 * Fires when this field hide.
23967 * @param {Roo.bootstrap.form.DateField} this
23968 * @param {Mixed} date The date value
23973 * Fires when select a date.
23974 * @param {Roo.bootstrap.form.DateField} this
23975 * @param {Mixed} date The date value
23981 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23984 * @cfg {String} format
23985 * The default time format string which can be overriden for localization support. The format must be
23986 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23991 getAutoCreate : function()
23993 this.after = '<i class="fa far fa-clock"></i>';
23994 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23998 onRender: function(ct, position)
24001 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24003 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24005 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24007 this.pop = this.picker().select('>.datepicker-time',true).first();
24008 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24010 this.picker().on('mousedown', this.onMousedown, this);
24011 this.picker().on('click', this.onClick, this);
24013 this.picker().addClass('datepicker-dropdown');
24018 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24019 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24020 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24021 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24022 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24023 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24027 fireKey: function(e){
24028 if (!this.picker().isVisible()){
24029 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24035 e.preventDefault();
24043 this.onTogglePeriod();
24046 this.onIncrementMinutes();
24049 this.onDecrementMinutes();
24058 onClick: function(e) {
24059 e.stopPropagation();
24060 e.preventDefault();
24063 picker : function()
24065 return this.pickerEl;
24068 fillTime: function()
24070 var time = this.pop.select('tbody', true).first();
24072 time.dom.innerHTML = '';
24087 cls: 'hours-up fa fas fa-chevron-up'
24107 cls: 'minutes-up fa fas fa-chevron-up'
24128 cls: 'timepicker-hour',
24143 cls: 'timepicker-minute',
24158 cls: 'btn btn-primary period',
24180 cls: 'hours-down fa fas fa-chevron-down'
24200 cls: 'minutes-down fa fas fa-chevron-down'
24217 // default minute is a multiple of minuteStep
24218 if(typeof(this.time) === 'undefined') {
24219 this.time = new Date();
24220 this.time = this.time.add(Date.MINUTE, Math.round(parseInt(this.time.format('i')) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i')));
24222 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24229 var hours = this.time.getHours();
24230 var minutes = this.time.getMinutes();
24243 hours = hours - 12;
24247 hours = '0' + hours;
24251 minutes = '0' + minutes;
24254 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24255 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24256 this.pop.select('button', true).first().dom.innerHTML = period;
24262 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24264 var cls = ['bottom'];
24266 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24273 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24277 //this.picker().setXY(20000,20000);
24278 this.picker().addClass(cls.join('-'));
24282 Roo.each(cls, function(c){
24287 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24288 //_this.picker().setTop(_this.inputEl().getHeight());
24292 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24294 //_this.picker().setTop(0 - _this.picker().getHeight());
24299 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24303 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24311 onFocus : function()
24313 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24317 onBlur : function()
24319 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24325 this.picker().show();
24330 this.fireEvent('show', this, this.date);
24335 this.picker().hide();
24338 this.fireEvent('hide', this, this.date);
24341 setTime : function()
24344 this.setValue(this.time.format(this.format));
24346 this.fireEvent('select', this, this.date);
24351 onMousedown: function(e){
24352 e.stopPropagation();
24353 e.preventDefault();
24356 onIncrementHours: function()
24358 Roo.log('onIncrementHours');
24359 this.time = this.time.add(Date.HOUR, 1);
24364 onDecrementHours: function()
24366 Roo.log('onDecrementHours');
24367 this.time = this.time.add(Date.HOUR, -1);
24371 onIncrementMinutes: function()
24373 Roo.log('onIncrementMinutes');
24374 var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24375 this.time = this.time.add(Date.MINUTE, minutesToAdd);
24379 onDecrementMinutes: function()
24381 Roo.log('onDecrementMinutes');
24382 var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24383 this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24387 onTogglePeriod: function()
24389 Roo.log('onTogglePeriod');
24390 this.time = this.time.add(Date.HOUR, 12);
24398 Roo.apply(Roo.bootstrap.form.TimeField, {
24402 cls: 'datepicker dropdown-menu',
24406 cls: 'datepicker-time',
24410 cls: 'table-condensed',
24439 cls: 'btn btn-info ok',
24467 * @class Roo.bootstrap.form.MonthField
24468 * @extends Roo.bootstrap.form.Input
24469 * Bootstrap MonthField class
24471 * @cfg {String} language default en
24474 * Create a new MonthField
24475 * @param {Object} config The config object
24478 Roo.bootstrap.form.MonthField = function(config){
24479 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24484 * Fires when this field show.
24485 * @param {Roo.bootstrap.form.MonthField} this
24486 * @param {Mixed} date The date value
24491 * Fires when this field hide.
24492 * @param {Roo.bootstrap.form.MonthField} this
24493 * @param {Mixed} date The date value
24498 * Fires when select a date.
24499 * @param {Roo.bootstrap.form.MonthField} this
24500 * @param {String} oldvalue The old value
24501 * @param {String} newvalue The new value
24507 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24509 onRender: function(ct, position)
24512 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24514 this.language = this.language || 'en';
24515 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24516 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24518 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24519 this.isInline = false;
24520 this.isInput = true;
24521 this.component = this.el.select('.add-on', true).first() || false;
24522 this.component = (this.component && this.component.length === 0) ? false : this.component;
24523 this.hasInput = this.component && this.inputEL().length;
24525 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24527 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24529 this.picker().on('mousedown', this.onMousedown, this);
24530 this.picker().on('click', this.onClick, this);
24532 this.picker().addClass('datepicker-dropdown');
24534 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24535 v.setStyle('width', '189px');
24542 if(this.isInline) {
24548 setValue: function(v, suppressEvent)
24550 var o = this.getValue();
24552 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24556 if(suppressEvent !== true){
24557 this.fireEvent('select', this, o, v);
24562 getValue: function()
24567 onClick: function(e)
24569 e.stopPropagation();
24570 e.preventDefault();
24572 var target = e.getTarget();
24574 if(target.nodeName.toLowerCase() === 'i'){
24575 target = Roo.get(target).dom.parentNode;
24578 var nodeName = target.nodeName;
24579 var className = target.className;
24580 var html = target.innerHTML;
24582 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24586 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24588 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24594 picker : function()
24596 return this.pickerEl;
24599 fillMonths: function()
24602 var months = this.picker().select('>.datepicker-months td', true).first();
24604 months.dom.innerHTML = '';
24610 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24613 months.createChild(month);
24622 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24623 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24626 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24627 e.removeClass('active');
24629 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24630 e.addClass('active');
24637 if(this.isInline) {
24641 this.picker().removeClass(['bottom', 'top']);
24643 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24645 * place to the top of element!
24649 this.picker().addClass('top');
24650 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24655 this.picker().addClass('bottom');
24657 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24660 onFocus : function()
24662 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24666 onBlur : function()
24668 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24670 var d = this.inputEl().getValue();
24679 this.picker().show();
24680 this.picker().select('>.datepicker-months', true).first().show();
24684 this.fireEvent('show', this, this.date);
24689 if(this.isInline) {
24692 this.picker().hide();
24693 this.fireEvent('hide', this, this.date);
24697 onMousedown: function(e)
24699 e.stopPropagation();
24700 e.preventDefault();
24705 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24709 fireKey: function(e)
24711 if (!this.picker().isVisible()){
24712 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24723 e.preventDefault();
24727 dir = e.keyCode == 37 ? -1 : 1;
24729 this.vIndex = this.vIndex + dir;
24731 if(this.vIndex < 0){
24735 if(this.vIndex > 11){
24739 if(isNaN(this.vIndex)){
24743 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24749 dir = e.keyCode == 38 ? -1 : 1;
24751 this.vIndex = this.vIndex + dir * 4;
24753 if(this.vIndex < 0){
24757 if(this.vIndex > 11){
24761 if(isNaN(this.vIndex)){
24765 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24770 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24771 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24775 e.preventDefault();
24778 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24779 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24795 this.picker().remove();
24800 Roo.apply(Roo.bootstrap.form.MonthField, {
24819 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24820 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24825 Roo.apply(Roo.bootstrap.form.MonthField, {
24829 cls: 'datepicker dropdown-menu roo-dynamic',
24833 cls: 'datepicker-months',
24837 cls: 'table-condensed',
24839 Roo.bootstrap.form.DateField.content
24859 * @class Roo.bootstrap.form.CheckBox
24860 * @extends Roo.bootstrap.form.Input
24861 * Bootstrap CheckBox class
24863 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24864 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24865 * @cfg {String} boxLabel The text that appears beside the checkbox
24866 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24867 * @cfg {Boolean} checked initnal the element
24868 * @cfg {Boolean} inline inline the element (default false)
24869 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24870 * @cfg {String} tooltip label tooltip
24873 * Create a new CheckBox
24874 * @param {Object} config The config object
24877 Roo.bootstrap.form.CheckBox = function(config){
24878 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24883 * Fires when the element is checked or unchecked.
24884 * @param {Roo.bootstrap.form.CheckBox} this This input
24885 * @param {Boolean} checked The new checked value
24890 * Fires when the element is click.
24891 * @param {Roo.bootstrap.form.CheckBox} this This input
24898 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24900 inputType: 'checkbox',
24909 // checkbox success does not make any sense really..
24914 getAutoCreate : function()
24916 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24922 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24925 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24931 type : this.inputType,
24932 value : this.inputValue,
24933 cls : 'roo-' + this.inputType, //'form-box',
24934 placeholder : this.placeholder || ''
24938 if(this.inputType != 'radio'){
24942 cls : 'roo-hidden-value',
24943 value : this.checked ? this.inputValue : this.valueOff
24948 if (this.weight) { // Validity check?
24949 cfg.cls += " " + this.inputType + "-" + this.weight;
24952 if (this.disabled) {
24953 input.disabled=true;
24957 input.checked = this.checked;
24962 input.name = this.name;
24964 if(this.inputType != 'radio'){
24965 hidden.name = this.name;
24966 input.name = '_hidden_' + this.name;
24971 input.cls += ' input-' + this.size;
24976 ['xs','sm','md','lg'].map(function(size){
24977 if (settings[size]) {
24978 cfg.cls += ' col-' + size + '-' + settings[size];
24982 var inputblock = input;
24984 if (this.before || this.after) {
24987 cls : 'input-group',
24992 inputblock.cn.push({
24994 cls : 'input-group-addon',
24999 inputblock.cn.push(input);
25001 if(this.inputType != 'radio'){
25002 inputblock.cn.push(hidden);
25006 inputblock.cn.push({
25008 cls : 'input-group-addon',
25014 var boxLabelCfg = false;
25020 //'for': id, // box label is handled by onclick - so no for...
25022 html: this.boxLabel
25025 boxLabelCfg.tooltip = this.tooltip;
25031 if (align ==='left' && this.fieldLabel.length) {
25032 // Roo.log("left and has label");
25037 cls : 'control-label',
25038 html : this.fieldLabel
25049 cfg.cn[1].cn.push(boxLabelCfg);
25052 if(this.labelWidth > 12){
25053 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25056 if(this.labelWidth < 13 && this.labelmd == 0){
25057 this.labelmd = this.labelWidth;
25060 if(this.labellg > 0){
25061 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25062 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25065 if(this.labelmd > 0){
25066 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25067 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25070 if(this.labelsm > 0){
25071 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25072 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25075 if(this.labelxs > 0){
25076 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25077 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25080 } else if ( this.fieldLabel.length) {
25081 // Roo.log(" label");
25085 tag: this.boxLabel ? 'span' : 'label',
25087 cls: 'control-label box-input-label',
25088 //cls : 'input-group-addon',
25089 html : this.fieldLabel
25096 cfg.cn.push(boxLabelCfg);
25101 // Roo.log(" no label && no align");
25102 cfg.cn = [ inputblock ] ;
25104 cfg.cn.push(boxLabelCfg);
25112 if(this.inputType != 'radio'){
25113 cfg.cn.push(hidden);
25121 * return the real input element.
25123 inputEl: function ()
25125 return this.el.select('input.roo-' + this.inputType,true).first();
25127 hiddenEl: function ()
25129 return this.el.select('input.roo-hidden-value',true).first();
25132 labelEl: function()
25134 return this.el.select('label.control-label',true).first();
25136 /* depricated... */
25140 return this.labelEl();
25143 boxLabelEl: function()
25145 return this.el.select('label.box-label',true).first();
25148 initEvents : function()
25150 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25152 this.inputEl().on('click', this.onClick, this);
25154 if (this.boxLabel) {
25155 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25158 this.startValue = this.getValue();
25161 Roo.bootstrap.form.CheckBox.register(this);
25165 onClick : function(e)
25167 if(this.fireEvent('click', this, e) !== false){
25168 this.setChecked(!this.checked);
25173 setChecked : function(state,suppressEvent)
25175 this.startValue = this.getValue();
25177 if(this.inputType == 'radio'){
25179 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25180 e.dom.checked = false;
25183 this.inputEl().dom.checked = true;
25185 this.inputEl().dom.value = this.inputValue;
25187 if(suppressEvent !== true){
25188 this.fireEvent('check', this, true);
25196 this.checked = state;
25198 this.inputEl().dom.checked = state;
25201 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25203 if(suppressEvent !== true){
25204 this.fireEvent('check', this, state);
25210 getValue : function()
25212 if(this.inputType == 'radio'){
25213 return this.getGroupValue();
25216 return this.hiddenEl().dom.value;
25220 getGroupValue : function()
25222 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25226 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25229 setValue : function(v,suppressEvent)
25231 if(this.inputType == 'radio'){
25232 this.setGroupValue(v, suppressEvent);
25236 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25241 setGroupValue : function(v, suppressEvent)
25243 this.startValue = this.getValue();
25245 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25246 e.dom.checked = false;
25248 if(e.dom.value == v){
25249 e.dom.checked = true;
25253 if(suppressEvent !== true){
25254 this.fireEvent('check', this, true);
25262 validate : function()
25264 if(this.getVisibilityEl().hasClass('hidden')){
25270 (this.inputType == 'radio' && this.validateRadio()) ||
25271 (this.inputType == 'checkbox' && this.validateCheckbox())
25277 this.markInvalid();
25281 validateRadio : function()
25283 if(this.getVisibilityEl().hasClass('hidden')){
25287 if(this.allowBlank){
25293 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25294 if(!e.dom.checked){
25306 validateCheckbox : function()
25309 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25310 //return (this.getValue() == this.inputValue) ? true : false;
25313 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25321 for(var i in group){
25322 if(group[i].el.isVisible(true)){
25330 for(var i in group){
25335 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25342 * Mark this field as valid
25344 markValid : function()
25348 this.fireEvent('valid', this);
25350 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25353 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25360 if(this.inputType == 'radio'){
25361 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25362 var fg = e.findParent('.form-group', false, true);
25363 if (Roo.bootstrap.version == 3) {
25364 fg.removeClass([_this.invalidClass, _this.validClass]);
25365 fg.addClass(_this.validClass);
25367 fg.removeClass(['is-valid', 'is-invalid']);
25368 fg.addClass('is-valid');
25376 var fg = this.el.findParent('.form-group', false, true);
25377 if (Roo.bootstrap.version == 3) {
25378 fg.removeClass([this.invalidClass, this.validClass]);
25379 fg.addClass(this.validClass);
25381 fg.removeClass(['is-valid', 'is-invalid']);
25382 fg.addClass('is-valid');
25387 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25393 for(var i in group){
25394 var fg = group[i].el.findParent('.form-group', false, true);
25395 if (Roo.bootstrap.version == 3) {
25396 fg.removeClass([this.invalidClass, this.validClass]);
25397 fg.addClass(this.validClass);
25399 fg.removeClass(['is-valid', 'is-invalid']);
25400 fg.addClass('is-valid');
25406 * Mark this field as invalid
25407 * @param {String} msg The validation message
25409 markInvalid : function(msg)
25411 if(this.allowBlank){
25417 this.fireEvent('invalid', this, msg);
25419 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25422 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25426 label.markInvalid();
25429 if(this.inputType == 'radio'){
25431 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25432 var fg = e.findParent('.form-group', false, true);
25433 if (Roo.bootstrap.version == 3) {
25434 fg.removeClass([_this.invalidClass, _this.validClass]);
25435 fg.addClass(_this.invalidClass);
25437 fg.removeClass(['is-invalid', 'is-valid']);
25438 fg.addClass('is-invalid');
25446 var fg = this.el.findParent('.form-group', false, true);
25447 if (Roo.bootstrap.version == 3) {
25448 fg.removeClass([_this.invalidClass, _this.validClass]);
25449 fg.addClass(_this.invalidClass);
25451 fg.removeClass(['is-invalid', 'is-valid']);
25452 fg.addClass('is-invalid');
25457 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25463 for(var i in group){
25464 var fg = group[i].el.findParent('.form-group', false, true);
25465 if (Roo.bootstrap.version == 3) {
25466 fg.removeClass([_this.invalidClass, _this.validClass]);
25467 fg.addClass(_this.invalidClass);
25469 fg.removeClass(['is-invalid', 'is-valid']);
25470 fg.addClass('is-invalid');
25476 clearInvalid : function()
25478 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25480 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25482 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25484 if (label && label.iconEl) {
25485 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25486 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25490 disable : function()
25492 if(this.inputType != 'radio'){
25493 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25500 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25501 _this.getActionEl().addClass(this.disabledClass);
25502 e.dom.disabled = true;
25506 this.disabled = true;
25507 this.fireEvent("disable", this);
25511 enable : function()
25513 if(this.inputType != 'radio'){
25514 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25521 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25522 _this.getActionEl().removeClass(this.disabledClass);
25523 e.dom.disabled = false;
25527 this.disabled = false;
25528 this.fireEvent("enable", this);
25532 setBoxLabel : function(v)
25537 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25543 Roo.apply(Roo.bootstrap.form.CheckBox, {
25548 * register a CheckBox Group
25549 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25551 register : function(checkbox)
25553 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25554 this.groups[checkbox.groupId] = {};
25557 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25561 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25565 * fetch a CheckBox Group based on the group ID
25566 * @param {string} the group ID
25567 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25569 get: function(groupId) {
25570 if (typeof(this.groups[groupId]) == 'undefined') {
25574 return this.groups[groupId] ;
25587 * @class Roo.bootstrap.form.Radio
25588 * @extends Roo.bootstrap.Component
25589 * Bootstrap Radio class
25590 * @cfg {String} boxLabel - the label associated
25591 * @cfg {String} value - the value of radio
25594 * Create a new Radio
25595 * @param {Object} config The config object
25597 Roo.bootstrap.form.Radio = function(config){
25598 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25602 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25608 getAutoCreate : function()
25612 cls : 'form-group radio',
25617 html : this.boxLabel
25625 initEvents : function()
25627 this.parent().register(this);
25629 this.el.on('click', this.onClick, this);
25633 onClick : function(e)
25635 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25636 this.setChecked(true);
25640 setChecked : function(state, suppressEvent)
25642 this.parent().setValue(this.value, suppressEvent);
25646 setBoxLabel : function(v)
25651 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25666 * @class Roo.bootstrap.form.SecurePass
25667 * @extends Roo.bootstrap.form.Input
25668 * Bootstrap SecurePass class
25672 * Create a new SecurePass
25673 * @param {Object} config The config object
25676 Roo.bootstrap.form.SecurePass = function (config) {
25677 // these go here, so the translation tool can replace them..
25679 PwdEmpty: "Please type a password, and then retype it to confirm.",
25680 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25681 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25682 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25683 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25684 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25685 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25686 TooWeak: "Your password is Too Weak."
25688 this.meterLabel = "Password strength:";
25689 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25690 this.meterClass = [
25691 "roo-password-meter-tooweak",
25692 "roo-password-meter-weak",
25693 "roo-password-meter-medium",
25694 "roo-password-meter-strong",
25695 "roo-password-meter-grey"
25700 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25703 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25705 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25707 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25708 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25709 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25710 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25711 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25712 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25713 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25723 * @cfg {String/Object} Label for the strength meter (defaults to
25724 * 'Password strength:')
25729 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25730 * ['Weak', 'Medium', 'Strong'])
25733 pwdStrengths: false,
25746 initEvents: function ()
25748 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25750 if (this.el.is('input[type=password]') && Roo.isSafari) {
25751 this.el.on('keydown', this.SafariOnKeyDown, this);
25754 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25757 onRender: function (ct, position)
25759 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25760 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25761 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25763 this.trigger.createChild({
25768 cls: 'roo-password-meter-grey col-xs-12',
25771 //width: this.meterWidth + 'px'
25775 cls: 'roo-password-meter-text'
25781 if (this.hideTrigger) {
25782 this.trigger.setDisplayed(false);
25784 this.setSize(this.width || '', this.height || '');
25787 onDestroy: function ()
25789 if (this.trigger) {
25790 this.trigger.removeAllListeners();
25791 this.trigger.remove();
25794 this.wrap.remove();
25796 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25799 checkStrength: function ()
25801 var pwd = this.inputEl().getValue();
25802 if (pwd == this._lastPwd) {
25807 if (this.ClientSideStrongPassword(pwd)) {
25809 } else if (this.ClientSideMediumPassword(pwd)) {
25811 } else if (this.ClientSideWeakPassword(pwd)) {
25817 Roo.log('strength1: ' + strength);
25819 //var pm = this.trigger.child('div/div/div').dom;
25820 var pm = this.trigger.child('div/div');
25821 pm.removeClass(this.meterClass);
25822 pm.addClass(this.meterClass[strength]);
25825 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25827 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25829 this._lastPwd = pwd;
25833 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25835 this._lastPwd = '';
25837 var pm = this.trigger.child('div/div');
25838 pm.removeClass(this.meterClass);
25839 pm.addClass('roo-password-meter-grey');
25842 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25845 this.inputEl().dom.type='password';
25848 validateValue: function (value)
25850 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25853 if (value.length == 0) {
25854 if (this.allowBlank) {
25855 this.clearInvalid();
25859 this.markInvalid(this.errors.PwdEmpty);
25860 this.errorMsg = this.errors.PwdEmpty;
25868 if (!value.match(/[\x21-\x7e]+/)) {
25869 this.markInvalid(this.errors.PwdBadChar);
25870 this.errorMsg = this.errors.PwdBadChar;
25873 if (value.length < 6) {
25874 this.markInvalid(this.errors.PwdShort);
25875 this.errorMsg = this.errors.PwdShort;
25878 if (value.length > 16) {
25879 this.markInvalid(this.errors.PwdLong);
25880 this.errorMsg = this.errors.PwdLong;
25884 if (this.ClientSideStrongPassword(value)) {
25886 } else if (this.ClientSideMediumPassword(value)) {
25888 } else if (this.ClientSideWeakPassword(value)) {
25895 if (strength < 2) {
25896 //this.markInvalid(this.errors.TooWeak);
25897 this.errorMsg = this.errors.TooWeak;
25902 console.log('strength2: ' + strength);
25904 //var pm = this.trigger.child('div/div/div').dom;
25906 var pm = this.trigger.child('div/div');
25907 pm.removeClass(this.meterClass);
25908 pm.addClass(this.meterClass[strength]);
25910 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25912 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25914 this.errorMsg = '';
25918 CharacterSetChecks: function (type)
25921 this.fResult = false;
25924 isctype: function (character, type)
25927 case this.kCapitalLetter:
25928 if (character >= 'A' && character <= 'Z') {
25933 case this.kSmallLetter:
25934 if (character >= 'a' && character <= 'z') {
25940 if (character >= '0' && character <= '9') {
25945 case this.kPunctuation:
25946 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25957 IsLongEnough: function (pwd, size)
25959 return !(pwd == null || isNaN(size) || pwd.length < size);
25962 SpansEnoughCharacterSets: function (word, nb)
25964 if (!this.IsLongEnough(word, nb))
25969 var characterSetChecks = new Array(
25970 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25971 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25974 for (var index = 0; index < word.length; ++index) {
25975 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25976 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25977 characterSetChecks[nCharSet].fResult = true;
25984 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25985 if (characterSetChecks[nCharSet].fResult) {
25990 if (nCharSets < nb) {
25996 ClientSideStrongPassword: function (pwd)
25998 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26001 ClientSideMediumPassword: function (pwd)
26003 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26006 ClientSideWeakPassword: function (pwd)
26008 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26011 });Roo.rtf = {}; // namespace
26012 Roo.rtf.Hex = function(hex)
26016 Roo.rtf.Paragraph = function(opts)
26018 this.content = []; ///??? is that used?
26019 };Roo.rtf.Span = function(opts)
26021 this.value = opts.value;
26024 Roo.rtf.Group = function(parent)
26026 // we dont want to acutally store parent - it will make debug a nightmare..
26034 Roo.rtf.Group.prototype = {
26038 addContent : function(node) {
26039 // could set styles...
26040 this.content.push(node);
26042 addChild : function(cn)
26046 // only for images really...
26047 toDataURL : function()
26049 var mimetype = false;
26051 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
26052 mimetype = "image/png";
26054 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26055 mimetype = "image/jpeg";
26058 return 'about:blank'; // ?? error?
26062 var hexstring = this.content[this.content.length-1].value;
26064 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26065 return String.fromCharCode(parseInt(a, 16));
26070 // this looks like it's normally the {rtf{ .... }}
26071 Roo.rtf.Document = function()
26073 // we dont want to acutally store parent - it will make debug a nightmare..
26079 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
26080 addChild : function(cn)
26084 case 'rtlch': // most content seems to be inside this??
26087 this.rtlch.push(cn);
26090 this[cn.type] = cn;
26095 getElementsByType : function(type)
26098 this._getElementsByType(type, ret, this.cn, 'rtf');
26101 _getElementsByType : function (type, ret, search_array, path)
26103 search_array.forEach(function(n,i) {
26104 if (n.type == type) {
26105 n.path = path + '/' + n.type + ':' + i;
26108 if (n.cn.length > 0) {
26109 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26116 Roo.rtf.Ctrl = function(opts)
26118 this.value = opts.value;
26119 this.param = opts.param;
26124 * based on this https://github.com/iarna/rtf-parser
26125 * it's really only designed to extract pict from pasted RTF
26129 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26138 Roo.rtf.Parser = function(text) {
26139 //super({objectMode: true})
26141 this.parserState = this.parseText;
26143 // these are for interpeter...
26145 ///this.parserState = this.parseTop
26146 this.groupStack = [];
26147 this.hexStore = [];
26150 this.groups = []; // where we put the return.
26152 for (var ii = 0; ii < text.length; ++ii) {
26155 if (text[ii] === '\n') {
26161 this.parserState(text[ii]);
26167 Roo.rtf.Parser.prototype = {
26168 text : '', // string being parsed..
26170 controlWordParam : '',
26174 groupStack : false,
26179 row : 1, // reportin?
26183 push : function (el)
26185 var m = 'cmd'+ el.type;
26186 if (typeof(this[m]) == 'undefined') {
26187 Roo.log('invalid cmd:' + el.type);
26193 flushHexStore : function()
26195 if (this.hexStore.length < 1) {
26198 var hexstr = this.hexStore.map(
26203 this.group.addContent( new Roo.rtf.Hex( hexstr ));
26206 this.hexStore.splice(0)
26210 cmdgroupstart : function()
26212 this.flushHexStore();
26214 this.groupStack.push(this.group);
26217 if (this.doc === false) {
26218 this.group = this.doc = new Roo.rtf.Document();
26222 this.group = new Roo.rtf.Group(this.group);
26224 cmdignorable : function()
26226 this.flushHexStore();
26227 this.group.ignorable = true;
26229 cmdendparagraph : function()
26231 this.flushHexStore();
26232 this.group.addContent(new Roo.rtf.Paragraph());
26234 cmdgroupend : function ()
26236 this.flushHexStore();
26237 var endingGroup = this.group;
26240 this.group = this.groupStack.pop();
26242 this.group.addChild(endingGroup);
26247 var doc = this.group || this.doc;
26248 //if (endingGroup instanceof FontTable) {
26249 // doc.fonts = endingGroup.table
26250 //} else if (endingGroup instanceof ColorTable) {
26251 // doc.colors = endingGroup.table
26252 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26253 if (endingGroup.ignorable === false) {
26255 this.groups.push(endingGroup);
26256 // Roo.log( endingGroup );
26258 //Roo.each(endingGroup.content, function(item)) {
26259 // doc.addContent(item);
26261 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26264 cmdtext : function (cmd)
26266 this.flushHexStore();
26267 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26268 //this.group = this.doc
26269 return; // we really don't care about stray text...
26271 this.group.addContent(new Roo.rtf.Span(cmd));
26273 cmdcontrolword : function (cmd)
26275 this.flushHexStore();
26276 if (!this.group.type) {
26277 this.group.type = cmd.value;
26280 this.group.addContent(new Roo.rtf.Ctrl(cmd));
26281 // we actually don't care about ctrl words...
26284 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26285 if (this[method]) {
26286 this[method](cmd.param)
26288 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26292 cmdhexchar : function(cmd) {
26293 this.hexStore.push(cmd);
26295 cmderror : function(cmd) {
26301 if (this.text !== '\u0000') this.emitText()
26307 parseText : function(c)
26310 this.parserState = this.parseEscapes;
26311 } else if (c === '{') {
26312 this.emitStartGroup();
26313 } else if (c === '}') {
26314 this.emitEndGroup();
26315 } else if (c === '\x0A' || c === '\x0D') {
26316 // cr/lf are noise chars
26322 parseEscapes: function (c)
26324 if (c === '\\' || c === '{' || c === '}') {
26326 this.parserState = this.parseText;
26328 this.parserState = this.parseControlSymbol;
26329 this.parseControlSymbol(c);
26332 parseControlSymbol: function(c)
26335 this.text += '\u00a0'; // nbsp
26336 this.parserState = this.parseText
26337 } else if (c === '-') {
26338 this.text += '\u00ad'; // soft hyphen
26339 } else if (c === '_') {
26340 this.text += '\u2011'; // non-breaking hyphen
26341 } else if (c === '*') {
26342 this.emitIgnorable();
26343 this.parserState = this.parseText;
26344 } else if (c === "'") {
26345 this.parserState = this.parseHexChar;
26346 } else if (c === '|') { // formula cacter
26347 this.emitFormula();
26348 this.parserState = this.parseText;
26349 } else if (c === ':') { // subentry in an index entry
26350 this.emitIndexSubEntry();
26351 this.parserState = this.parseText;
26352 } else if (c === '\x0a') {
26353 this.emitEndParagraph();
26354 this.parserState = this.parseText;
26355 } else if (c === '\x0d') {
26356 this.emitEndParagraph();
26357 this.parserState = this.parseText;
26359 this.parserState = this.parseControlWord;
26360 this.parseControlWord(c);
26363 parseHexChar: function (c)
26365 if (/^[A-Fa-f0-9]$/.test(c)) {
26367 if (this.hexChar.length >= 2) {
26368 this.emitHexChar();
26369 this.parserState = this.parseText;
26373 this.emitError("Invalid character \"" + c + "\" in hex literal.");
26374 this.parserState = this.parseText;
26377 parseControlWord : function(c)
26380 this.emitControlWord();
26381 this.parserState = this.parseText;
26382 } else if (/^[-\d]$/.test(c)) {
26383 this.parserState = this.parseControlWordParam;
26384 this.controlWordParam += c;
26385 } else if (/^[A-Za-z]$/.test(c)) {
26386 this.controlWord += c;
26388 this.emitControlWord();
26389 this.parserState = this.parseText;
26393 parseControlWordParam : function (c) {
26394 if (/^\d$/.test(c)) {
26395 this.controlWordParam += c;
26396 } else if (c === ' ') {
26397 this.emitControlWord();
26398 this.parserState = this.parseText;
26400 this.emitControlWord();
26401 this.parserState = this.parseText;
26409 emitText : function () {
26410 if (this.text === '') {
26422 emitControlWord : function ()
26425 if (this.controlWord === '') {
26426 // do we want to track this - it seems just to cause problems.
26427 //this.emitError('empty control word');
26430 type: 'controlword',
26431 value: this.controlWord,
26432 param: this.controlWordParam !== '' && Number(this.controlWordParam),
26438 this.controlWord = '';
26439 this.controlWordParam = '';
26441 emitStartGroup : function ()
26445 type: 'groupstart',
26451 emitEndGroup : function ()
26461 emitIgnorable : function ()
26471 emitHexChar : function ()
26476 value: this.hexChar,
26483 emitError : function (message)
26491 char: this.cpos //,
26492 //stack: new Error().stack
26495 emitEndParagraph : function () {
26498 type: 'endparagraph',
26507 * @class Roo.htmleditor.Filter
26508 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26509 * @cfg {DomElement} node The node to iterate and filter
26510 * @cfg {boolean|String|Array} tag Tags to replace
26512 * Create a new Filter.
26513 * @param {Object} config Configuration options
26518 Roo.htmleditor.Filter = function(cfg) {
26519 Roo.apply(this.cfg);
26520 // this does not actually call walk as it's really just a abstract class
26524 Roo.htmleditor.Filter.prototype = {
26530 // overrride to do replace comments.
26531 replaceComment : false,
26533 // overrride to do replace or do stuff with tags..
26534 replaceTag : false,
26536 walk : function(dom)
26538 Roo.each( Array.from(dom.childNodes), function( e ) {
26541 case e.nodeType == 8 && this.replaceComment !== false: // comment
26542 this.replaceComment(e);
26545 case e.nodeType != 1: //not a node.
26548 case this.tag === true: // everything
26549 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26550 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26551 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26552 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26553 if (this.replaceTag && false === this.replaceTag(e)) {
26556 if (e.hasChildNodes()) {
26561 default: // tags .. that do not match.
26562 if (e.hasChildNodes()) {
26572 removeNodeKeepChildren : function( node)
26575 ar = Array.from(node.childNodes);
26576 for (var i = 0; i < ar.length; i++) {
26578 node.removeChild(ar[i]);
26579 // what if we need to walk these???
26580 node.parentNode.insertBefore(ar[i], node);
26583 node.parentNode.removeChild(node);
26588 * @class Roo.htmleditor.FilterAttributes
26589 * clean attributes and styles including http:// etc.. in attribute
26591 * Run a new Attribute Filter
26592 * @param {Object} config Configuration options
26594 Roo.htmleditor.FilterAttributes = function(cfg)
26596 Roo.apply(this, cfg);
26597 this.attrib_black = this.attrib_black || [];
26598 this.attrib_white = this.attrib_white || [];
26600 this.attrib_clean = this.attrib_clean || [];
26601 this.style_white = this.style_white || [];
26602 this.style_black = this.style_black || [];
26603 this.walk(cfg.node);
26606 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26608 tag: true, // all tags
26610 attrib_black : false, // array
26611 attrib_clean : false,
26612 attrib_white : false,
26614 style_white : false,
26615 style_black : false,
26618 replaceTag : function(node)
26620 if (!node.attributes || !node.attributes.length) {
26624 for (var i = node.attributes.length-1; i > -1 ; i--) {
26625 var a = node.attributes[i];
26627 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26628 node.removeAttribute(a.name);
26634 if (a.name.toLowerCase().substr(0,2)=='on') {
26635 node.removeAttribute(a.name);
26640 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26641 node.removeAttribute(a.name);
26644 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26645 this.cleanAttr(node,a.name,a.value); // fixme..
26648 if (a.name == 'style') {
26649 this.cleanStyle(node,a.name,a.value);
26652 /// clean up MS crap..
26653 // tecnically this should be a list of valid class'es..
26656 if (a.name == 'class') {
26657 if (a.value.match(/^Mso/)) {
26658 node.removeAttribute('class');
26661 if (a.value.match(/^body$/)) {
26662 node.removeAttribute('class');
26672 return true; // clean children
26675 cleanAttr: function(node, n,v)
26678 if (v.match(/^\./) || v.match(/^\//)) {
26681 if (v.match(/^(http|https):\/\//)
26682 || v.match(/^mailto:/)
26683 || v.match(/^ftp:/)
26684 || v.match(/^data:/)
26688 if (v.match(/^#/)) {
26691 if (v.match(/^\{/)) { // allow template editing.
26694 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26695 node.removeAttribute(n);
26698 cleanStyle : function(node, n,v)
26700 if (v.match(/expression/)) { //XSS?? should we even bother..
26701 node.removeAttribute(n);
26705 var parts = v.split(/;/);
26708 Roo.each(parts, function(p) {
26709 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26713 var l = p.split(':').shift().replace(/\s+/g,'');
26714 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26716 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26720 // only allow 'c whitelisted system attributes'
26721 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26729 if (clean.length) {
26730 node.setAttribute(n, clean.join(';'));
26732 node.removeAttribute(n);
26741 * @class Roo.htmleditor.FilterBlack
26742 * remove blacklisted elements.
26744 * Run a new Blacklisted Filter
26745 * @param {Object} config Configuration options
26748 Roo.htmleditor.FilterBlack = function(cfg)
26750 Roo.apply(this, cfg);
26751 this.walk(cfg.node);
26754 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26756 tag : true, // all elements.
26758 replaceTag : function(n)
26760 n.parentNode.removeChild(n);
26764 * @class Roo.htmleditor.FilterComment
26767 * Run a new Comments Filter
26768 * @param {Object} config Configuration options
26770 Roo.htmleditor.FilterComment = function(cfg)
26772 this.walk(cfg.node);
26775 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26778 replaceComment : function(n)
26780 n.parentNode.removeChild(n);
26783 * @class Roo.htmleditor.FilterKeepChildren
26784 * remove tags but keep children
26786 * Run a new Keep Children Filter
26787 * @param {Object} config Configuration options
26790 Roo.htmleditor.FilterKeepChildren = function(cfg)
26792 Roo.apply(this, cfg);
26793 if (this.tag === false) {
26794 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26797 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26798 this.cleanNamespace = true;
26801 this.walk(cfg.node);
26804 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26806 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26808 replaceTag : function(node)
26810 // walk children...
26811 //Roo.log(node.tagName);
26812 var ar = Array.from(node.childNodes);
26815 for (var i = 0; i < ar.length; i++) {
26817 if (e.nodeType == 1) {
26819 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26820 || // array and it matches
26821 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26823 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26825 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26827 this.replaceTag(ar[i]); // child is blacklisted as well...
26832 ar = Array.from(node.childNodes);
26833 for (var i = 0; i < ar.length; i++) {
26835 node.removeChild(ar[i]);
26836 // what if we need to walk these???
26837 node.parentNode.insertBefore(ar[i], node);
26838 if (this.tag !== false) {
26843 //Roo.log("REMOVE:" + node.tagName);
26844 node.parentNode.removeChild(node);
26845 return false; // don't walk children
26850 * @class Roo.htmleditor.FilterParagraph
26851 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26852 * like on 'push' to remove the <p> tags and replace them with line breaks.
26854 * Run a new Paragraph Filter
26855 * @param {Object} config Configuration options
26858 Roo.htmleditor.FilterParagraph = function(cfg)
26860 // no need to apply config.
26861 this.walk(cfg.node);
26864 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26871 replaceTag : function(node)
26874 if (node.childNodes.length == 1 &&
26875 node.childNodes[0].nodeType == 3 &&
26876 node.childNodes[0].textContent.trim().length < 1
26878 // remove and replace with '<BR>';
26879 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26880 return false; // no need to walk..
26882 var ar = Array.from(node.childNodes);
26883 for (var i = 0; i < ar.length; i++) {
26884 node.removeChild(ar[i]);
26885 // what if we need to walk these???
26886 node.parentNode.insertBefore(ar[i], node);
26888 // now what about this?
26892 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26893 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26894 node.parentNode.removeChild(node);
26901 * @class Roo.htmleditor.FilterSpan
26902 * filter span's with no attributes out..
26904 * Run a new Span Filter
26905 * @param {Object} config Configuration options
26908 Roo.htmleditor.FilterSpan = function(cfg)
26910 // no need to apply config.
26911 this.walk(cfg.node);
26914 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26920 replaceTag : function(node)
26922 if (node.attributes && node.attributes.length > 0) {
26923 return true; // walk if there are any.
26925 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26931 * @class Roo.htmleditor.FilterTableWidth
26932 try and remove table width data - as that frequently messes up other stuff.
26934 * was cleanTableWidths.
26936 * Quite often pasting from word etc.. results in tables with column and widths.
26937 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26940 * Run a new Table Filter
26941 * @param {Object} config Configuration options
26944 Roo.htmleditor.FilterTableWidth = function(cfg)
26946 // no need to apply config.
26947 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26948 this.walk(cfg.node);
26951 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26956 replaceTag: function(node) {
26960 if (node.hasAttribute('width')) {
26961 node.removeAttribute('width');
26965 if (node.hasAttribute("style")) {
26968 var styles = node.getAttribute("style").split(";");
26970 Roo.each(styles, function(s) {
26971 if (!s.match(/:/)) {
26974 var kv = s.split(":");
26975 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26978 // what ever is left... we allow.
26981 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26982 if (!nstyle.length) {
26983 node.removeAttribute('style');
26987 return true; // continue doing children..
26990 * @class Roo.htmleditor.FilterWord
26991 * try and clean up all the mess that Word generates.
26993 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26996 * Run a new Span Filter
26997 * @param {Object} config Configuration options
27000 Roo.htmleditor.FilterWord = function(cfg)
27002 // no need to apply config.
27003 this.replaceDocBullets(cfg.node);
27005 this.replaceAname(cfg.node);
27006 // this is disabled as the removal is done by other filters;
27007 // this.walk(cfg.node);
27008 this.replaceImageTable(cfg.node);
27012 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27018 * Clean up MS wordisms...
27020 replaceTag : function(node)
27023 // no idea what this does - span with text, replaceds with just text.
27025 node.nodeName == 'SPAN' &&
27026 !node.hasAttributes() &&
27027 node.childNodes.length == 1 &&
27028 node.firstChild.nodeName == "#text"
27030 var textNode = node.firstChild;
27031 node.removeChild(textNode);
27032 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27033 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27035 node.parentNode.insertBefore(textNode, node);
27036 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27037 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27040 node.parentNode.removeChild(node);
27041 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27046 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27047 node.parentNode.removeChild(node);
27048 return false; // dont do chidlren
27050 //Roo.log(node.tagName);
27051 // remove - but keep children..
27052 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27053 //Roo.log('-- removed');
27054 while (node.childNodes.length) {
27055 var cn = node.childNodes[0];
27056 node.removeChild(cn);
27057 node.parentNode.insertBefore(cn, node);
27058 // move node to parent - and clean it..
27059 if (cn.nodeType == 1) {
27060 this.replaceTag(cn);
27064 node.parentNode.removeChild(node);
27065 /// no need to iterate chidlren = it's got none..
27066 //this.iterateChildren(node, this.cleanWord);
27067 return false; // no need to iterate children.
27070 if (node.className.length) {
27072 var cn = node.className.split(/\W+/);
27074 Roo.each(cn, function(cls) {
27075 if (cls.match(/Mso[a-zA-Z]+/)) {
27080 node.className = cna.length ? cna.join(' ') : '';
27082 node.removeAttribute("class");
27086 if (node.hasAttribute("lang")) {
27087 node.removeAttribute("lang");
27090 if (node.hasAttribute("style")) {
27092 var styles = node.getAttribute("style").split(";");
27094 Roo.each(styles, function(s) {
27095 if (!s.match(/:/)) {
27098 var kv = s.split(":");
27099 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27102 // what ever is left... we allow.
27105 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27106 if (!nstyle.length) {
27107 node.removeAttribute('style');
27110 return true; // do children
27116 styleToObject: function(node)
27118 var styles = (node.getAttribute("style") || '').split(";");
27120 Roo.each(styles, function(s) {
27121 if (!s.match(/:/)) {
27124 var kv = s.split(":");
27126 // what ever is left... we allow.
27127 ret[kv[0].trim()] = kv[1];
27133 replaceAname : function (doc)
27135 // replace all the a/name without..
27136 var aa = Array.from(doc.getElementsByTagName('a'));
27137 for (var i = 0; i < aa.length; i++) {
27139 if (a.hasAttribute("name")) {
27140 a.removeAttribute("name");
27142 if (a.hasAttribute("href")) {
27145 // reparent children.
27146 this.removeNodeKeepChildren(a);
27156 replaceDocBullets : function(doc)
27158 // this is a bit odd - but it appears some indents use ql-indent-1
27159 //Roo.log(doc.innerHTML);
27161 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27162 for( var i = 0; i < listpara.length; i ++) {
27163 listpara[i].className = "MsoListParagraph";
27166 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27167 for( var i = 0; i < listpara.length; i ++) {
27168 listpara[i].className = "MsoListParagraph";
27170 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27171 for( var i = 0; i < listpara.length; i ++) {
27172 listpara[i].className = "MsoListParagraph";
27174 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
27175 for( var i = 0; i < listpara.length; i ++) {
27176 listpara[i].className = "MsoListParagraph";
27179 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27180 var htwo = Array.from(doc.getElementsByTagName('h2'));
27181 for( var i = 0; i < htwo.length; i ++) {
27182 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27183 htwo[i].className = "MsoListParagraph";
27186 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
27187 for( var i = 0; i < listpara.length; i ++) {
27188 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27189 listpara[i].className = "MsoListParagraph";
27191 listpara[i].className = "MsoNormalx";
27195 listpara = doc.getElementsByClassName('MsoListParagraph');
27196 // Roo.log(doc.innerHTML);
27200 while(listpara.length) {
27202 this.replaceDocBullet(listpara.item(0));
27209 replaceDocBullet : function(p)
27211 // gather all the siblings.
27213 parent = p.parentNode,
27214 doc = parent.ownerDocument,
27217 //Roo.log("Parsing: " + p.innerText) ;
27218 var listtype = 'ul';
27220 if (ns.nodeType != 1) {
27221 ns = ns.nextSibling;
27224 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27225 //Roo.log("Missing para r q1indent - got:" + ns.className);
27228 var spans = ns.getElementsByTagName('span');
27230 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27232 ns = ns.nextSibling;
27234 if (!spans.length) {
27239 for (var i = 0; i < spans.length;i++) {
27241 if (se.hasAttribute('style') && se.hasAttribute('style') && se.style.fontFamily != '') {
27242 ff = se.style.fontFamily;
27248 //Roo.log("got font family: " + ff);
27249 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27255 //Roo.log("no mso-list?");
27257 var spans = ns.getElementsByTagName('span');
27258 if (!spans.length) {
27261 var has_list = false;
27262 for(var i = 0; i < spans.length; i++) {
27263 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27272 ns = ns.nextSibling;
27276 if (!items.length) {
27281 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27282 parent.insertBefore(ul, p);
27284 var stack = [ ul ];
27285 var last_li = false;
27287 var margin_to_depth = {};
27290 items.forEach(function(n, ipos) {
27291 //Roo.log("got innertHMLT=" + n.innerHTML);
27293 var spans = n.getElementsByTagName('span');
27294 if (!spans.length) {
27295 //Roo.log("No spans found");
27297 parent.removeChild(n);
27300 return; // skip it...
27306 for(var i = 0; i < spans.length; i++) {
27308 style = this.styleToObject(spans[i]);
27309 if (typeof(style['mso-list']) == 'undefined') {
27312 if (listtype == 'ol') {
27313 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
27315 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27318 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27319 style = this.styleToObject(n); // mo-list is from the parent node.
27320 if (typeof(style['mso-list']) == 'undefined') {
27321 //Roo.log("parent is missing level");
27323 parent.removeChild(n);
27328 var margin = style['margin-left'];
27329 if (typeof(margin_to_depth[margin]) == 'undefined') {
27331 margin_to_depth[margin] = max_margins;
27333 nlvl = margin_to_depth[margin] ;
27337 var nul = doc.createElement(listtype); // what about number lists...
27339 last_li = doc.createElement('li');
27340 stack[lvl].appendChild(last_li);
27342 last_li.appendChild(nul);
27348 // not starting at 1..
27349 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27350 stack[nlvl].setAttribute("start", num);
27353 var nli = stack[nlvl].appendChild(doc.createElement('li'));
27355 nli.innerHTML = n.innerHTML;
27356 //Roo.log("innerHTML = " + n.innerHTML);
27357 parent.removeChild(n);
27369 replaceImageTable : function(doc)
27372 <table cellpadding=0 cellspacing=0 align=left>
27374 <td width=423 height=0></td>
27378 <td><img width=601 height=401
27379 src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27380 v:shapes="Picture_x0020_2"></td>
27384 var imgs = Array.from(doc.getElementsByTagName('img'));
27385 Roo.each(imgs, function(img) {
27386 var td = img.parentNode;
27387 if (td.nodeName != 'TD') {
27390 var tr = td.parentNode;
27391 if (tr.nodeName != 'TR') {
27394 var tbody = tr.parentNode;
27395 if (tbody.nodeName != 'TBODY') {
27398 var table = tbody.parentNode;
27399 if (table.nodeName != 'TABLE') {
27404 if (table.getElementsByTagName('tr').length != 2) {
27407 if (table.getElementsByTagName('td').length != 3) {
27410 if (table.innerText.trim() != '') {
27413 var p = table.parentNode;
27414 img.parentNode.removeChild(img);
27415 p.insertBefore(img, table);
27416 p.removeChild(table);
27427 * @class Roo.htmleditor.FilterStyleToTag
27428 * part of the word stuff... - certain 'styles' should be converted to tags.
27430 * font-weight: bold -> bold
27431 * ?? super / subscrit etc..
27434 * Run a new style to tag filter.
27435 * @param {Object} config Configuration options
27437 Roo.htmleditor.FilterStyleToTag = function(cfg)
27441 B : [ 'fontWeight' , 'bold'],
27442 I : [ 'fontStyle' , 'italic'],
27443 //pre : [ 'font-style' , 'italic'],
27444 // h1.. h6 ?? font-size?
27445 SUP : [ 'verticalAlign' , 'super' ],
27446 SUB : [ 'verticalAlign' , 'sub' ]
27451 Roo.apply(this, cfg);
27454 this.walk(cfg.node);
27461 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27463 tag: true, // all tags
27468 replaceTag : function(node)
27472 if (node.getAttribute("style") === null) {
27476 for (var k in this.tags) {
27477 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27479 node.style.removeProperty(this.tags[k][0]);
27482 if (!inject.length) {
27485 var cn = Array.from(node.childNodes);
27487 Roo.each(inject, function(t) {
27488 var nc = node.ownerDocument.createElement(t);
27489 nn.appendChild(nc);
27492 for(var i = 0;i < cn.length;cn++) {
27493 node.removeChild(cn[i]);
27494 nn.appendChild(cn[i]);
27496 return true /// iterate thru
27500 * @class Roo.htmleditor.FilterLongBr
27501 * BR/BR/BR - keep a maximum of 2...
27503 * Run a new Long BR Filter
27504 * @param {Object} config Configuration options
27507 Roo.htmleditor.FilterLongBr = function(cfg)
27509 // no need to apply config.
27510 this.walk(cfg.node);
27513 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27520 replaceTag : function(node)
27523 var ps = node.nextSibling;
27524 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27525 ps = ps.nextSibling;
27528 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
27529 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27533 if (!ps || ps.nodeType != 1) {
27537 if (!ps || ps.tagName != 'BR') {
27546 if (!node.previousSibling) {
27549 var ps = node.previousSibling;
27551 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27552 ps = ps.previousSibling;
27554 if (!ps || ps.nodeType != 1) {
27557 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27558 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27562 node.parentNode.removeChild(node); // remove me...
27564 return false; // no need to do children
27571 * @class Roo.htmleditor.FilterBlock
27572 * removes id / data-block and contenteditable that are associated with blocks
27573 * usage should be done on a cloned copy of the dom
27575 * Run a new Attribute Filter { node : xxxx }}
27576 * @param {Object} config Configuration options
27578 Roo.htmleditor.FilterBlock = function(cfg)
27580 Roo.apply(this, cfg);
27581 var qa = cfg.node.querySelectorAll;
27582 this.removeAttributes('data-block');
27583 this.removeAttributes('contenteditable');
27584 this.removeAttributes('id');
27588 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27590 node: true, // all tags
27593 removeAttributes : function(attr)
27595 var ar = this.node.querySelectorAll('*[' + attr + ']');
27596 for (var i =0;i<ar.length;i++) {
27597 ar[i].removeAttribute(attr);
27606 * This is based loosely on tinymce
27607 * @class Roo.htmleditor.TidySerializer
27608 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27610 * @method Serializer
27611 * @param {Object} settings Name/value settings object.
27615 Roo.htmleditor.TidySerializer = function(settings)
27617 Roo.apply(this, settings);
27619 this.writer = new Roo.htmleditor.TidyWriter(settings);
27624 Roo.htmleditor.TidySerializer.prototype = {
27627 * @param {boolean} inner do the inner of the node.
27634 * Serializes the specified node into a string.
27637 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27638 * @method serialize
27639 * @param {DomElement} node Node instance to serialize.
27640 * @return {String} String with HTML based on DOM tree.
27642 serialize : function(node) {
27644 // = settings.validate;
27645 var writer = this.writer;
27649 3: function(node) {
27651 writer.text(node.nodeValue, node);
27654 8: function(node) {
27655 writer.comment(node.nodeValue);
27657 // Processing instruction
27658 7: function(node) {
27659 writer.pi(node.name, node.nodeValue);
27662 10: function(node) {
27663 writer.doctype(node.nodeValue);
27666 4: function(node) {
27667 writer.cdata(node.nodeValue);
27669 // Document fragment
27670 11: function(node) {
27671 node = node.firstChild;
27677 node = node.nextSibling
27682 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27683 return writer.getContent();
27686 walk: function(node)
27688 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27689 handler = this.handlers[node.nodeType];
27696 var name = node.nodeName;
27697 var isEmpty = node.childNodes.length < 1;
27699 var writer = this.writer;
27700 var attrs = node.attributes;
27703 writer.start(node.nodeName, attrs, isEmpty, node);
27707 node = node.firstChild;
27714 node = node.nextSibling;
27720 // Serialize element and treat all non elements as fragments
27725 * This is based loosely on tinymce
27726 * @class Roo.htmleditor.TidyWriter
27727 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27730 * - not tested much with 'PRE' formated elements.
27736 Roo.htmleditor.TidyWriter = function(settings)
27739 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27740 Roo.apply(this, settings);
27744 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27747 Roo.htmleditor.TidyWriter.prototype = {
27754 // part of state...
27758 last_inline : false,
27763 * Writes the a start element such as <p id="a">.
27766 * @param {String} name Name of the element.
27767 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27768 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27770 start: function(name, attrs, empty, node)
27772 var i, l, attr, value;
27774 // there are some situations where adding line break && indentation will not work. will not work.
27775 // <span / b / i ... formating?
27777 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27778 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27780 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27782 var add_lb = name == 'BR' ? false : in_inline;
27784 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27788 var indentstr = this.indentstr;
27790 // e_inline = elements that can be inline, but still allow \n before and after?
27791 // only 'BR' ??? any others?
27793 // ADD LINE BEFORE tage
27794 if (!this.in_pre) {
27797 if (name == 'BR') {
27799 } else if (this.lastElementEndsWS()) {
27802 // otherwise - no new line. (and dont indent.)
27813 this.html.push(indentstr + '<', name.toLowerCase());
27816 for (i = 0, l = attrs.length; i < l; i++) {
27818 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27824 this.html[this.html.length] = '/>';
27826 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27828 var e_inline = name == 'BR' ? false : this.in_inline;
27830 if (!e_inline && !this.in_pre) {
27837 this.html[this.html.length] = '>';
27839 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27841 if (!in_inline && !in_pre) {
27842 var cn = node.firstChild;
27844 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27848 cn = cn.nextSibling;
27856 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27858 in_inline : in_inline
27860 // add a line after if we are not in a
27862 if (!in_inline && !in_pre) {
27871 lastElementEndsWS : function()
27873 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27874 if (value === false) {
27877 return value.match(/\s+$/);
27882 * Writes the a end element such as </p>.
27885 * @param {String} name Name of the element.
27887 end: function(name) {
27890 var indentstr = '';
27891 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27893 if (!this.in_pre && !in_inline) {
27895 indentstr = this.indentstr;
27897 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27898 this.last_inline = in_inline;
27900 // pop the indent state..
27903 * Writes a text node.
27905 * In pre - we should not mess with the contents.
27909 * @param {String} text String to write out.
27910 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27912 text: function(in_text, node)
27914 // if not in whitespace critical
27915 if (in_text.length < 1) {
27918 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27921 this.html[this.html.length] = text;
27925 if (this.in_inline) {
27926 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27928 text = text.replace(/\s+/,' '); // all white space to single white space
27931 // if next tag is '<BR>', then we can trim right..
27932 if (node.nextSibling &&
27933 node.nextSibling.nodeType == 1 &&
27934 node.nextSibling.nodeName == 'BR' )
27936 text = text.replace(/\s+$/g,'');
27938 // if previous tag was a BR, we can also trim..
27939 if (node.previousSibling &&
27940 node.previousSibling.nodeType == 1 &&
27941 node.previousSibling.nodeName == 'BR' )
27943 text = this.indentstr + text.replace(/^\s+/g,'');
27945 if (text.match(/\n/)) {
27946 text = text.replace(
27947 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27949 // remoeve the last whitespace / line break.
27950 text = text.replace(/\n\s+$/,'');
27952 // repace long lines
27956 this.html[this.html.length] = text;
27959 // see if previous element was a inline element.
27960 var indentstr = this.indentstr;
27962 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27964 // should trim left?
27965 if (node.previousSibling &&
27966 node.previousSibling.nodeType == 1 &&
27967 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27973 text = text.replace(/^\s+/,''); // trim left
27976 // should trim right?
27977 if (node.nextSibling &&
27978 node.nextSibling.nodeType == 1 &&
27979 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27984 text = text.replace(/\s+$/,''); // trim right
27991 if (text.length < 1) {
27994 if (!text.match(/\n/)) {
27995 this.html.push(indentstr + text);
27999 text = this.indentstr + text.replace(
28000 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
28002 // remoeve the last whitespace / line break.
28003 text = text.replace(/\s+$/,'');
28005 this.html.push(text);
28007 // split and indent..
28012 * Writes a cdata node such as <![CDATA[data]]>.
28015 * @param {String} text String to write out inside the cdata.
28017 cdata: function(text) {
28018 this.html.push('<![CDATA[', text, ']]>');
28021 * Writes a comment node such as <!-- Comment -->.
28024 * @param {String} text String to write out inside the comment.
28026 comment: function(text) {
28027 this.html.push('<!--', text, '-->');
28030 * Writes a PI node such as <?xml attr="value" ?>.
28033 * @param {String} name Name of the pi.
28034 * @param {String} text String to write out inside the pi.
28036 pi: function(name, text) {
28037 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28038 this.indent != '' && this.html.push('\n');
28041 * Writes a doctype node such as <!DOCTYPE data>.
28044 * @param {String} text String to write out inside the doctype.
28046 doctype: function(text) {
28047 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28050 * Resets the internal buffer if one wants to reuse the writer.
28054 reset: function() {
28055 this.html.length = 0;
28064 * Returns the contents that got serialized.
28066 * @method getContent
28067 * @return {String} HTML contents that got written down.
28069 getContent: function() {
28070 return this.html.join('').replace(/\n$/, '');
28073 pushState : function(cfg)
28075 this.state.push(cfg);
28076 Roo.apply(this, cfg);
28079 popState : function()
28081 if (this.state.length < 1) {
28082 return; // nothing to push
28089 if (this.state.length > 0) {
28090 cfg = this.state[this.state.length-1];
28092 Roo.apply(this, cfg);
28095 addLine: function()
28097 if (this.html.length < 1) {
28102 var value = this.html[this.html.length - 1];
28103 if (value.length > 0 && '\n' !== value) {
28104 this.html.push('\n');
28109 //'pre script noscript style textarea video audio iframe object code'
28110 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
28114 Roo.htmleditor.TidyWriter.inline_elements = [
28115 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28116 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28118 Roo.htmleditor.TidyWriter.shortend_elements = [
28119 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28120 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28123 Roo.htmleditor.TidyWriter.whitespace_elements = [
28124 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28126 * This is based loosely on tinymce
28127 * @class Roo.htmleditor.TidyEntities
28129 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28131 * Not 100% sure this is actually used or needed.
28134 Roo.htmleditor.TidyEntities = {
28137 * initialize data..
28139 init : function (){
28141 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28146 buildEntitiesLookup: function(items, radix) {
28147 var i, chr, entity, lookup = {};
28151 items = typeof(items) == 'string' ? items.split(',') : items;
28152 radix = radix || 10;
28153 // Build entities lookup table
28154 for (i = 0; i < items.length; i += 2) {
28155 chr = String.fromCharCode(parseInt(items[i], radix));
28156 // Only add non base entities
28157 if (!this.baseEntities[chr]) {
28158 entity = '&' + items[i + 1] + ';';
28159 lookup[chr] = entity;
28160 lookup[entity] = chr;
28199 // Needs to be escaped since the YUI compressor would otherwise break the code
28206 // Reverse lookup table for raw entities
28207 reverseEntities : {
28215 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28216 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28217 rawCharsRegExp : /[<>&\"\']/g,
28218 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28219 namedEntities : false,
28220 namedEntitiesData : [
28721 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28723 * @method encodeRaw
28724 * @param {String} text Text to encode.
28725 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28726 * @return {String} Entity encoded text.
28728 encodeRaw: function(text, attr)
28731 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28732 return t.baseEntities[chr] || chr;
28736 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28737 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28738 * and is exposed as the DOMUtils.encode function.
28740 * @method encodeAllRaw
28741 * @param {String} text Text to encode.
28742 * @return {String} Entity encoded text.
28744 encodeAllRaw: function(text) {
28746 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28747 return t.baseEntities[chr] || chr;
28751 * Encodes the specified string using numeric entities. The core entities will be
28752 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28754 * @method encodeNumeric
28755 * @param {String} text Text to encode.
28756 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28757 * @return {String} Entity encoded text.
28759 encodeNumeric: function(text, attr) {
28761 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28762 // Multi byte sequence convert it to a single entity
28763 if (chr.length > 1) {
28764 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28766 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28770 * Encodes the specified string using named entities. The core entities will be encoded
28771 * as named ones but all non lower ascii characters will be encoded into named entities.
28773 * @method encodeNamed
28774 * @param {String} text Text to encode.
28775 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28776 * @param {Object} entities Optional parameter with entities to use.
28777 * @return {String} Entity encoded text.
28779 encodeNamed: function(text, attr, entities) {
28781 entities = entities || this.namedEntities;
28782 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28783 return t.baseEntities[chr] || entities[chr] || chr;
28787 * Returns an encode function based on the name(s) and it's optional entities.
28789 * @method getEncodeFunc
28790 * @param {String} name Comma separated list of encoders for example named,numeric.
28791 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28792 * @return {function} Encode function to be used.
28794 getEncodeFunc: function(name, entities) {
28795 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28797 function encodeNamedAndNumeric(text, attr) {
28798 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28799 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28803 function encodeCustomNamed(text, attr) {
28804 return t.encodeNamed(text, attr, entities);
28806 // Replace + with , to be compatible with previous TinyMCE versions
28807 name = this.makeMap(name.replace(/\+/g, ','));
28808 // Named and numeric encoder
28809 if (name.named && name.numeric) {
28810 return this.encodeNamedAndNumeric;
28816 return encodeCustomNamed;
28818 return this.encodeNamed;
28821 if (name.numeric) {
28822 return this.encodeNumeric;
28825 return this.encodeRaw;
28828 * Decodes the specified string, this will replace entities with raw UTF characters.
28831 * @param {String} text Text to entity decode.
28832 * @return {String} Entity decoded string.
28834 decode: function(text)
28837 return text.replace(this.entityRegExp, function(all, numeric) {
28839 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28840 // Support upper UTF
28841 if (numeric > 65535) {
28843 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28845 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28847 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28850 nativeDecode : function (text) {
28853 makeMap : function (items, delim, map) {
28855 items = items || [];
28856 delim = delim || ',';
28857 if (typeof items == "string") {
28858 items = items.split(delim);
28863 map[items[i]] = {};
28871 Roo.htmleditor.TidyEntities.init();
28873 * @class Roo.htmleditor.KeyEnter
28874 * Handle Enter press..
28875 * @cfg {Roo.HtmlEditorCore} core the editor.
28877 * Create a new Filter.
28878 * @param {Object} config Configuration options
28885 Roo.htmleditor.KeyEnter = function(cfg) {
28886 Roo.apply(this, cfg);
28887 // this does not actually call walk as it's really just a abstract class
28889 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28892 //Roo.htmleditor.KeyEnter.i = 0;
28895 Roo.htmleditor.KeyEnter.prototype = {
28899 keypress : function(e)
28901 if (e.charCode != 13 && e.charCode != 10) {
28902 Roo.log([e.charCode,e]);
28905 e.preventDefault();
28906 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28907 var doc = this.core.doc;
28911 var sel = this.core.getSelection();
28912 var range = sel.getRangeAt(0);
28913 var n = range.commonAncestorContainer;
28914 var pc = range.closest([ 'ol', 'ul']);
28915 var pli = range.closest('li');
28916 if (!pc || e.ctrlKey) {
28917 // on it list, or ctrl pressed.
28919 sel.insertNode('br', 'after');
28921 // only do this if we have ctrl key..
28922 var br = doc.createElement('br');
28923 br.className = 'clear';
28924 br.setAttribute('style', 'clear: both');
28925 sel.insertNode(br, 'after');
28929 this.core.undoManager.addEvent();
28930 this.core.fireEditorEvent(e);
28934 // deal with <li> insetion
28935 if (pli.innerText.trim() == '' &&
28936 pli.previousSibling &&
28937 pli.previousSibling.nodeName == 'LI' &&
28938 pli.previousSibling.innerText.trim() == '') {
28939 pli.parentNode.removeChild(pli.previousSibling);
28940 sel.cursorAfter(pc);
28941 this.core.undoManager.addEvent();
28942 this.core.fireEditorEvent(e);
28946 var li = doc.createElement('LI');
28947 li.innerHTML = ' ';
28948 if (!pli || !pli.firstSibling) {
28949 pc.appendChild(li);
28951 pli.parentNode.insertBefore(li, pli.firstSibling);
28953 sel.cursorText (li.firstChild);
28955 this.core.undoManager.addEvent();
28956 this.core.fireEditorEvent(e);
28968 * @class Roo.htmleditor.Block
28969 * Base class for html editor blocks - do not use it directly .. extend it..
28970 * @cfg {DomElement} node The node to apply stuff to.
28971 * @cfg {String} friendly_name the name that appears in the context bar about this block
28972 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28975 * Create a new Filter.
28976 * @param {Object} config Configuration options
28979 Roo.htmleditor.Block = function(cfg)
28981 // do nothing .. should not be called really.
28984 * factory method to get the block from an element (using cache if necessary)
28986 * @param {HtmlElement} the dom element
28988 Roo.htmleditor.Block.factory = function(node)
28990 var cc = Roo.htmleditor.Block.cache;
28991 var id = Roo.get(node).id;
28992 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28993 Roo.htmleditor.Block.cache[id].readElement(node);
28994 return Roo.htmleditor.Block.cache[id];
28996 var db = node.getAttribute('data-block');
28998 db = node.nodeName.toLowerCase().toUpperCaseFirst();
29000 var cls = Roo.htmleditor['Block' + db];
29001 if (typeof(cls) == 'undefined') {
29002 //Roo.log(node.getAttribute('data-block'));
29003 Roo.log("OOps missing block : " + 'Block' + db);
29006 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
29007 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
29011 * initalize all Elements from content that are 'blockable'
29013 * @param the body element
29015 Roo.htmleditor.Block.initAll = function(body, type)
29017 if (typeof(type) == 'undefined') {
29018 var ia = Roo.htmleditor.Block.initAll;
29024 Roo.each(Roo.get(body).query(type), function(e) {
29025 Roo.htmleditor.Block.factory(e);
29028 // question goes here... do we need to clear out this cache sometimes?
29029 // or show we make it relivant to the htmleditor.
29030 Roo.htmleditor.Block.cache = {};
29032 Roo.htmleditor.Block.prototype = {
29036 // used by context menu
29037 friendly_name : 'Based Block',
29039 // text for button to delete this element
29040 deleteTitle : false,
29044 * Update a node with values from this object
29045 * @param {DomElement} node
29047 updateElement : function(node)
29049 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29052 * convert to plain HTML for calling insertAtCursor..
29054 toHTML : function()
29056 return Roo.DomHelper.markup(this.toObject());
29059 * used by readEleemnt to extract data from a node
29060 * may need improving as it's pretty basic
29062 * @param {DomElement} node
29063 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29064 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29065 * @param {String} style the style property - eg. text-align
29067 getVal : function(node, tag, attr, style)
29070 if (tag !== true && n.tagName != tag.toUpperCase()) {
29071 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29072 // but kiss for now.
29073 n = node.getElementsByTagName(tag).item(0);
29078 if (attr === false) {
29081 if (attr == 'html') {
29082 return n.innerHTML;
29084 if (attr == 'style') {
29085 return n.style[style];
29088 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29092 * create a DomHelper friendly object - for use with
29093 * Roo.DomHelper.markup / overwrite / etc..
29096 toObject : function()
29101 * Read a node that has a 'data-block' property - and extract the values from it.
29102 * @param {DomElement} node - the node
29104 readElement : function(node)
29115 * @class Roo.htmleditor.BlockFigure
29116 * Block that has an image and a figcaption
29117 * @cfg {String} image_src the url for the image
29118 * @cfg {String} align (left|right) alignment for the block default left
29119 * @cfg {String} caption the text to appear below (and in the alt tag)
29120 * @cfg {String} caption_display (block|none) display or not the caption
29121 * @cfg {String|number} image_width the width of the image number or %?
29122 * @cfg {String|number} image_height the height of the image number or %?
29125 * Create a new Filter.
29126 * @param {Object} config Configuration options
29129 Roo.htmleditor.BlockFigure = function(cfg)
29132 this.readElement(cfg.node);
29133 this.updateElement(cfg.node);
29135 Roo.apply(this, cfg);
29137 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29144 caption_display : 'block',
29150 // margin: '2%', not used
29152 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
29155 // used by context menu
29156 friendly_name : 'Image with caption',
29157 deleteTitle : "Delete Image and Caption",
29159 contextMenu : function(toolbar)
29162 var block = function() {
29163 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29167 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29169 var syncValue = toolbar.editorcore.syncValue;
29175 xtype : 'TextItem',
29177 xns : rooui.Toolbar //Boostrap?
29181 text: 'Change Image URL',
29184 click: function (btn, state)
29188 Roo.MessageBox.show({
29189 title : "Image Source URL",
29190 msg : "Enter the url for the image",
29191 buttons: Roo.MessageBox.OKCANCEL,
29192 fn: function(btn, val){
29199 toolbar.editorcore.onEditorEvent();
29203 //multiline: multiline,
29205 value : b.image_src
29209 xns : rooui.Toolbar
29214 text: 'Change Link URL',
29217 click: function (btn, state)
29221 Roo.MessageBox.show({
29222 title : "Link URL",
29223 msg : "Enter the url for the link - leave blank to have no link",
29224 buttons: Roo.MessageBox.OKCANCEL,
29225 fn: function(btn, val){
29232 toolbar.editorcore.onEditorEvent();
29236 //multiline: multiline,
29242 xns : rooui.Toolbar
29246 text: 'Show Video URL',
29249 click: function (btn, state)
29251 Roo.MessageBox.alert("Video URL",
29252 block().video_url == '' ? 'This image is not linked ot a video' :
29253 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29256 xns : rooui.Toolbar
29261 xtype : 'TextItem',
29263 xns : rooui.Toolbar //Boostrap?
29266 xtype : 'ComboBox',
29267 allowBlank : false,
29268 displayField : 'val',
29271 triggerAction : 'all',
29273 valueField : 'val',
29277 select : function (combo, r, index)
29279 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29281 b.width = r.get('val');
29284 toolbar.editorcore.onEditorEvent();
29289 xtype : 'SimpleStore',
29302 xtype : 'TextItem',
29304 xns : rooui.Toolbar //Boostrap?
29307 xtype : 'ComboBox',
29308 allowBlank : false,
29309 displayField : 'val',
29312 triggerAction : 'all',
29314 valueField : 'val',
29318 select : function (combo, r, index)
29320 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29322 b.align = r.get('val');
29325 toolbar.editorcore.onEditorEvent();
29330 xtype : 'SimpleStore',
29344 text: 'Hide Caption',
29345 name : 'caption_display',
29347 enableToggle : true,
29348 setValue : function(v) {
29349 // this trigger toggle.
29351 this.setText(v ? "Hide Caption" : "Show Caption");
29352 this.setPressed(v != 'block');
29355 toggle: function (btn, state)
29358 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29359 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29362 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29363 toolbar.editorcore.onEditorEvent();
29366 xns : rooui.Toolbar
29372 * create a DomHelper friendly object - for use with
29373 * Roo.DomHelper.markup / overwrite / etc..
29375 toObject : function()
29377 var d = document.createElement('div');
29378 d.innerHTML = this.caption;
29380 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
29382 var iw = this.align == 'center' ? this.width : '100%';
29385 contenteditable : 'false',
29386 src : this.image_src,
29387 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29390 maxWidth : iw + ' !important', // this is not getting rendered?
29396 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29398 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
29403 if (this.href.length > 0) {
29407 contenteditable : 'true',
29415 if (this.video_url.length > 0) {
29420 allowfullscreen : true,
29421 width : 420, // these are for video tricks - that we replace the outer
29423 src : this.video_url,
29429 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29430 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29435 'data-block' : 'Figure',
29436 'data-width' : this.width,
29437 'data-caption' : this.caption,
29438 contenteditable : 'false',
29442 float : this.align ,
29443 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29444 width : this.align == 'center' ? '100%' : this.width,
29446 padding: this.align == 'center' ? '0' : '0 10px' ,
29447 textAlign : this.align // seems to work for email..
29452 align : this.align,
29458 'data-display' : this.caption_display,
29460 textAlign : 'left',
29462 lineHeight : '24px',
29463 display : this.caption_display,
29464 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
29466 width: this.align == 'center' ? this.width : '100%'
29470 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
29475 marginTop : '16px',
29481 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
29483 contenteditable : true,
29499 readElement : function(node)
29501 // this should not really come from the link...
29502 this.video_url = this.getVal(node, 'div', 'src');
29503 this.cls = this.getVal(node, 'div', 'class');
29504 this.href = this.getVal(node, 'a', 'href');
29507 this.image_src = this.getVal(node, 'img', 'src');
29509 this.align = this.getVal(node, 'figure', 'align');
29511 /// not really used - as hidden captions do not store the content here..
29512 var figcaption = this.getVal(node, 'figcaption', false);
29513 if (figcaption !== '') {
29514 this.caption = this.getVal(figcaption, 'i', 'html');
29518 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29519 var dc = this.getVal(node, true, 'data-caption');
29520 if (dc && dc.length) {
29523 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29524 this.width = this.getVal(node, true, 'data-width');
29525 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29528 removeNode : function()
29545 * @class Roo.htmleditor.BlockTable
29546 * Block that manages a table
29549 * Create a new Filter.
29550 * @param {Object} config Configuration options
29553 Roo.htmleditor.BlockTable = function(cfg)
29556 this.readElement(cfg.node);
29557 this.updateElement(cfg.node);
29559 Roo.apply(this, cfg);
29562 for(var r = 0; r < this.no_row; r++) {
29564 for(var c = 0; c < this.no_col; c++) {
29565 this.rows[r][c] = this.emptyCell();
29572 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29581 // used by context menu
29582 friendly_name : 'Table',
29583 deleteTitle : 'Delete Table',
29584 // context menu is drawn once..
29586 contextMenu : function(toolbar)
29589 var block = function() {
29590 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29594 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29596 var syncValue = toolbar.editorcore.syncValue;
29602 xtype : 'TextItem',
29604 xns : rooui.Toolbar //Boostrap?
29607 xtype : 'ComboBox',
29608 allowBlank : false,
29609 displayField : 'val',
29612 triggerAction : 'all',
29614 valueField : 'val',
29618 select : function (combo, r, index)
29620 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29622 b.width = r.get('val');
29625 toolbar.editorcore.onEditorEvent();
29630 xtype : 'SimpleStore',
29642 xtype : 'TextItem',
29643 text : "Columns: ",
29644 xns : rooui.Toolbar //Boostrap?
29651 click : function (_self, e)
29653 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29654 block().removeColumn();
29656 toolbar.editorcore.onEditorEvent();
29659 xns : rooui.Toolbar
29665 click : function (_self, e)
29667 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29668 block().addColumn();
29670 toolbar.editorcore.onEditorEvent();
29673 xns : rooui.Toolbar
29677 xtype : 'TextItem',
29679 xns : rooui.Toolbar //Boostrap?
29686 click : function (_self, e)
29688 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29689 block().removeRow();
29691 toolbar.editorcore.onEditorEvent();
29694 xns : rooui.Toolbar
29700 click : function (_self, e)
29704 toolbar.editorcore.onEditorEvent();
29707 xns : rooui.Toolbar
29712 text: 'Reset Column Widths',
29715 click : function (_self, e)
29717 block().resetWidths();
29719 toolbar.editorcore.onEditorEvent();
29722 xns : rooui.Toolbar
29733 * create a DomHelper friendly object - for use with
29734 * Roo.DomHelper.markup / overwrite / etc..
29735 * ?? should it be called with option to hide all editing features?
29737 toObject : function()
29742 contenteditable : 'false', // this stops cell selection from picking the table.
29743 'data-block' : 'Table',
29746 border : 'solid 1px #000', // ??? hard coded?
29747 'border-collapse' : 'collapse'
29750 { tag : 'tbody' , cn : [] }
29754 // do we have a head = not really
29756 Roo.each(this.rows, function( row ) {
29761 border : 'solid 1px #000',
29767 ret.cn[0].cn.push(tr);
29768 // does the row have any properties? ?? height?
29770 Roo.each(row, function( cell ) {
29774 contenteditable : 'true',
29775 'data-block' : 'Td',
29779 if (cell.colspan > 1) {
29780 td.colspan = cell.colspan ;
29781 nc += cell.colspan;
29785 if (cell.rowspan > 1) {
29786 td.rowspan = cell.rowspan ;
29795 ncols = Math.max(nc, ncols);
29799 // add the header row..
29808 readElement : function(node)
29810 node = node ? node : this.node ;
29811 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29815 var trs = Array.from(node.rows);
29816 trs.forEach(function(tr) {
29818 this.rows.push(row);
29822 Array.from(tr.cells).forEach(function(td) {
29825 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29826 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29827 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29828 html : td.innerHTML
29830 no_column += add.colspan;
29837 this.no_col = Math.max(this.no_col, no_column);
29844 normalizeRows: function()
29848 this.rows.forEach(function(row) {
29851 row = this.normalizeRow(row);
29853 row.forEach(function(c) {
29854 while (typeof(ret[rid][cid]) != 'undefined') {
29857 if (typeof(ret[rid]) == 'undefined') {
29863 if (c.rowspan < 2) {
29867 for(var i = 1 ;i < c.rowspan; i++) {
29868 if (typeof(ret[rid+i]) == 'undefined') {
29871 ret[rid+i][cid] = c;
29879 normalizeRow: function(row)
29882 row.forEach(function(c) {
29883 if (c.colspan < 2) {
29887 for(var i =0 ;i < c.colspan; i++) {
29895 deleteColumn : function(sel)
29897 if (!sel || sel.type != 'col') {
29900 if (this.no_col < 2) {
29904 this.rows.forEach(function(row) {
29905 var cols = this.normalizeRow(row);
29906 var col = cols[sel.col];
29907 if (col.colspan > 1) {
29917 removeColumn : function()
29919 this.deleteColumn({
29921 col : this.no_col-1
29923 this.updateElement();
29927 addColumn : function()
29930 this.rows.forEach(function(row) {
29931 row.push(this.emptyCell());
29934 this.updateElement();
29937 deleteRow : function(sel)
29939 if (!sel || sel.type != 'row') {
29943 if (this.no_row < 2) {
29947 var rows = this.normalizeRows();
29950 rows[sel.row].forEach(function(col) {
29951 if (col.rowspan > 1) {
29954 col.remove = 1; // flage it as removed.
29959 this.rows.forEach(function(row) {
29961 row.forEach(function(c) {
29962 if (typeof(c.remove) == 'undefined') {
29967 if (newrow.length > 0) {
29971 this.rows = newrows;
29976 this.updateElement();
29979 removeRow : function()
29983 row : this.no_row-1
29989 addRow : function()
29993 for (var i = 0; i < this.no_col; i++ ) {
29995 row.push(this.emptyCell());
29998 this.rows.push(row);
29999 this.updateElement();
30003 // the default cell object... at present...
30004 emptyCell : function() {
30005 return (new Roo.htmleditor.BlockTd({})).toObject();
30010 removeNode : function()
30017 resetWidths : function()
30019 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30020 var nn = Roo.htmleditor.Block.factory(n);
30022 nn.updateElement(n);
30035 * since selections really work on the table cell, then editing really should work from there
30037 * The original plan was to support merging etc... - but that may not be needed yet..
30039 * So this simple version will support:
30041 * adjust the width +/-
30042 * reset the width...
30051 * @class Roo.htmleditor.BlockTable
30052 * Block that manages a table
30055 * Create a new Filter.
30056 * @param {Object} config Configuration options
30059 Roo.htmleditor.BlockTd = function(cfg)
30062 this.readElement(cfg.node);
30063 this.updateElement(cfg.node);
30065 Roo.apply(this, cfg);
30070 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30075 textAlign : 'left',
30082 // used by context menu
30083 friendly_name : 'Table Cell',
30084 deleteTitle : false, // use our customer delete
30086 // context menu is drawn once..
30088 contextMenu : function(toolbar)
30091 var cell = function() {
30092 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30095 var table = function() {
30096 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30100 var saveSel = function()
30102 lr = toolbar.editorcore.getSelection().getRangeAt(0);
30104 var restoreSel = function()
30108 toolbar.editorcore.focus();
30109 var cr = toolbar.editorcore.getSelection();
30110 cr.removeAllRanges();
30112 toolbar.editorcore.onEditorEvent();
30113 }).defer(10, this);
30119 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30121 var syncValue = toolbar.editorcore.syncValue;
30128 text : 'Edit Table',
30130 click : function() {
30131 var t = toolbar.tb.selectedNode.closest('table');
30132 toolbar.editorcore.selectNode(t);
30133 toolbar.editorcore.onEditorEvent();
30142 xtype : 'TextItem',
30143 text : "Column Width: ",
30144 xns : rooui.Toolbar
30151 click : function (_self, e)
30153 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30154 cell().shrinkColumn();
30156 toolbar.editorcore.onEditorEvent();
30159 xns : rooui.Toolbar
30165 click : function (_self, e)
30167 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30168 cell().growColumn();
30170 toolbar.editorcore.onEditorEvent();
30173 xns : rooui.Toolbar
30177 xtype : 'TextItem',
30178 text : "Vertical Align: ",
30179 xns : rooui.Toolbar //Boostrap?
30182 xtype : 'ComboBox',
30183 allowBlank : false,
30184 displayField : 'val',
30187 triggerAction : 'all',
30189 valueField : 'val',
30193 select : function (combo, r, index)
30195 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30197 b.valign = r.get('val');
30200 toolbar.editorcore.onEditorEvent();
30205 xtype : 'SimpleStore',
30209 ['bottom'] // there are afew more...
30217 xtype : 'TextItem',
30218 text : "Merge Cells: ",
30219 xns : rooui.Toolbar
30228 click : function (_self, e)
30230 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30231 cell().mergeRight();
30232 //block().growColumn();
30234 toolbar.editorcore.onEditorEvent();
30237 xns : rooui.Toolbar
30244 click : function (_self, e)
30246 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30247 cell().mergeBelow();
30248 //block().growColumn();
30250 toolbar.editorcore.onEditorEvent();
30253 xns : rooui.Toolbar
30256 xtype : 'TextItem',
30258 xns : rooui.Toolbar
30266 click : function (_self, e)
30268 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30271 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30272 toolbar.editorcore.onEditorEvent();
30276 xns : rooui.Toolbar
30280 xns : rooui.Toolbar
30289 xns : rooui.Toolbar,
30298 click : function (_self, e)
30302 cell().deleteColumn();
30304 toolbar.editorcore.selectNode(t.node);
30305 toolbar.editorcore.onEditorEvent();
30314 click : function (_self, e)
30317 cell().deleteRow();
30320 toolbar.editorcore.selectNode(t.node);
30321 toolbar.editorcore.onEditorEvent();
30328 xtype : 'Separator',
30335 click : function (_self, e)
30338 var nn = t.node.nextSibling || t.node.previousSibling;
30339 t.node.parentNode.removeChild(t.node);
30341 toolbar.editorcore.selectNode(nn, true);
30343 toolbar.editorcore.onEditorEvent();
30353 // align... << fixme
30361 * create a DomHelper friendly object - for use with
30362 * Roo.DomHelper.markup / overwrite / etc..
30363 * ?? should it be called with option to hide all editing features?
30366 * create a DomHelper friendly object - for use with
30367 * Roo.DomHelper.markup / overwrite / etc..
30368 * ?? should it be called with option to hide all editing features?
30370 toObject : function()
30374 contenteditable : 'true', // this stops cell selection from picking the table.
30375 'data-block' : 'Td',
30376 valign : this.valign,
30378 'text-align' : this.textAlign,
30379 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30380 'border-collapse' : 'collapse',
30381 padding : '6px', // 8 for desktop / 4 for mobile
30382 'vertical-align': this.valign
30386 if (this.width != '') {
30387 ret.width = this.width;
30388 ret.style.width = this.width;
30392 if (this.colspan > 1) {
30393 ret.colspan = this.colspan ;
30395 if (this.rowspan > 1) {
30396 ret.rowspan = this.rowspan ;
30405 readElement : function(node)
30407 node = node ? node : this.node ;
30408 this.width = node.style.width;
30409 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30410 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30411 this.html = node.innerHTML;
30412 if (node.style.textAlign != '') {
30413 this.textAlign = node.style.textAlign;
30419 // the default cell object... at present...
30420 emptyCell : function() {
30424 textAlign : 'left',
30425 html : " " // is this going to be editable now?
30430 removeNode : function()
30432 return this.node.closest('table');
30440 toTableArray : function()
30443 var tab = this.node.closest('tr').closest('table');
30444 Array.from(tab.rows).forEach(function(r, ri){
30448 this.colWidths = [];
30449 var all_auto = true;
30450 Array.from(tab.rows).forEach(function(r, ri){
30453 Array.from(r.cells).forEach(function(ce, ci){
30458 colspan : ce.colSpan,
30459 rowspan : ce.rowSpan
30461 if (ce.isEqualNode(this.node)) {
30464 // if we have been filled up by a row?
30465 if (typeof(ret[rn][cn]) != 'undefined') {
30466 while(typeof(ret[rn][cn]) != 'undefined') {
30472 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30473 this.colWidths[cn] = ce.style.width;
30474 if (this.colWidths[cn] != '') {
30480 if (c.colspan < 2 && c.rowspan < 2 ) {
30485 for(var j = 0; j < c.rowspan; j++) {
30486 if (typeof(ret[rn+j]) == 'undefined') {
30487 continue; // we have a problem..
30490 for(var i = 0; i < c.colspan; i++) {
30491 ret[rn+j][cn+i] = c;
30500 // initalize widths.?
30501 // either all widths or no widths..
30503 this.colWidths[0] = false; // no widths flag.
30514 mergeRight: function()
30517 // get the contents of the next cell along..
30518 var tr = this.node.closest('tr');
30519 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30520 if (i >= tr.childNodes.length - 1) {
30521 return; // no cells on right to merge with.
30523 var table = this.toTableArray();
30525 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30526 return; // nothing right?
30528 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30529 // right cell - must be same rowspan and on the same row.
30530 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30531 return; // right hand side is not same rowspan.
30536 this.node.innerHTML += ' ' + rc.cell.innerHTML;
30537 tr.removeChild(rc.cell);
30538 this.colspan += rc.colspan;
30539 this.node.setAttribute('colspan', this.colspan);
30541 var table = this.toTableArray();
30542 this.normalizeWidths(table);
30543 this.updateWidths(table);
30547 mergeBelow : function()
30549 var table = this.toTableArray();
30550 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30551 return; // no row below
30553 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30554 return; // nothing right?
30556 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30558 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30559 return; // right hand side is not same rowspan.
30561 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30562 rc.cell.parentNode.removeChild(rc.cell);
30563 this.rowspan += rc.rowspan;
30564 this.node.setAttribute('rowspan', this.rowspan);
30569 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30572 var table = this.toTableArray();
30573 var cd = this.cellData;
30577 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30580 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30581 if (r == cd.row && c == cd.col) {
30582 this.node.removeAttribute('rowspan');
30583 this.node.removeAttribute('colspan');
30586 var ntd = this.node.cloneNode(); // which col/row should be 0..
30587 ntd.removeAttribute('id');
30588 ntd.style.width = this.colWidths[c];
30589 ntd.innerHTML = '';
30590 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30594 this.redrawAllCells(table);
30600 redrawAllCells: function(table)
30604 var tab = this.node.closest('tr').closest('table');
30605 var ctr = tab.rows[0].parentNode;
30606 Array.from(tab.rows).forEach(function(r, ri){
30608 Array.from(r.cells).forEach(function(ce, ci){
30609 ce.parentNode.removeChild(ce);
30611 r.parentNode.removeChild(r);
30613 for(var r = 0 ; r < table.length; r++) {
30614 var re = tab.rows[r];
30616 var re = tab.ownerDocument.createElement('tr');
30617 ctr.appendChild(re);
30618 for(var c = 0 ; c < table[r].length; c++) {
30619 if (table[r][c].cell === false) {
30623 re.appendChild(table[r][c].cell);
30625 table[r][c].cell = false;
30630 updateWidths : function(table)
30632 for(var r = 0 ; r < table.length; r++) {
30634 for(var c = 0 ; c < table[r].length; c++) {
30635 if (table[r][c].cell === false) {
30639 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30640 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30641 el.width = Math.floor(this.colWidths[c]) +'%';
30642 el.updateElement(el.node);
30644 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30645 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30647 for(var i = 0; i < table[r][c].colspan; i ++) {
30648 width += Math.floor(this.colWidths[c + i]);
30650 el.width = width +'%';
30651 el.updateElement(el.node);
30653 table[r][c].cell = false; // done
30657 normalizeWidths : function(table)
30659 if (this.colWidths[0] === false) {
30660 var nw = 100.0 / this.colWidths.length;
30661 this.colWidths.forEach(function(w,i) {
30662 this.colWidths[i] = nw;
30667 var t = 0, missing = [];
30669 this.colWidths.forEach(function(w,i) {
30671 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30672 var add = this.colWidths[i];
30681 var nc = this.colWidths.length;
30682 if (missing.length) {
30683 var mult = (nc - missing.length) / (1.0 * nc);
30685 var ew = (100 -t) / (1.0 * missing.length);
30686 this.colWidths.forEach(function(w,i) {
30688 this.colWidths[i] = w * mult;
30692 this.colWidths[i] = ew;
30694 // have to make up numbers..
30697 // now we should have all the widths..
30702 shrinkColumn : function()
30704 var table = this.toTableArray();
30705 this.normalizeWidths(table);
30706 var col = this.cellData.col;
30707 var nw = this.colWidths[col] * 0.8;
30711 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30712 this.colWidths.forEach(function(w,i) {
30714 this.colWidths[i] = nw;
30717 this.colWidths[i] += otherAdd
30719 this.updateWidths(table);
30722 growColumn : function()
30724 var table = this.toTableArray();
30725 this.normalizeWidths(table);
30726 var col = this.cellData.col;
30727 var nw = this.colWidths[col] * 1.2;
30731 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30732 this.colWidths.forEach(function(w,i) {
30734 this.colWidths[i] = nw;
30737 this.colWidths[i] -= otherSub
30739 this.updateWidths(table);
30742 deleteRow : function()
30744 // delete this rows 'tr'
30745 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30746 // then reduce the rowspan.
30747 var table = this.toTableArray();
30748 // this.cellData.row;
30749 for (var i =0;i< table[this.cellData.row].length ; i++) {
30750 var c = table[this.cellData.row][i];
30751 if (c.row != this.cellData.row) {
30754 c.cell.setAttribute('rowspan', c.rowspan);
30757 if (c.rowspan > 1) {
30759 c.cell.setAttribute('rowspan', c.rowspan);
30762 table.splice(this.cellData.row,1);
30763 this.redrawAllCells(table);
30766 deleteColumn : function()
30768 var table = this.toTableArray();
30770 for (var i =0;i< table.length ; i++) {
30771 var c = table[i][this.cellData.col];
30772 if (c.col != this.cellData.col) {
30773 table[i][this.cellData.col].colspan--;
30774 } else if (c.colspan > 1) {
30776 c.cell.setAttribute('colspan', c.colspan);
30778 table[i].splice(this.cellData.col,1);
30781 this.redrawAllCells(table);
30789 //<script type="text/javascript">
30792 * Based Ext JS Library 1.1.1
30793 * Copyright(c) 2006-2007, Ext JS, LLC.
30799 * @class Roo.HtmlEditorCore
30800 * @extends Roo.Component
30801 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30803 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30806 Roo.HtmlEditorCore = function(config){
30809 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30814 * @event initialize
30815 * Fires when the editor is fully initialized (including the iframe)
30816 * @param {Roo.HtmlEditorCore} this
30821 * Fires when the editor is first receives the focus. Any insertion must wait
30822 * until after this event.
30823 * @param {Roo.HtmlEditorCore} this
30827 * @event beforesync
30828 * Fires before the textarea is updated with content from the editor iframe. Return false
30829 * to cancel the sync.
30830 * @param {Roo.HtmlEditorCore} this
30831 * @param {String} html
30835 * @event beforepush
30836 * Fires before the iframe editor is updated with content from the textarea. Return false
30837 * to cancel the push.
30838 * @param {Roo.HtmlEditorCore} this
30839 * @param {String} html
30844 * Fires when the textarea is updated with content from the editor iframe.
30845 * @param {Roo.HtmlEditorCore} this
30846 * @param {String} html
30851 * Fires when the iframe editor is updated with content from the textarea.
30852 * @param {Roo.HtmlEditorCore} this
30853 * @param {String} html
30858 * @event editorevent
30859 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30860 * @param {Roo.HtmlEditorCore} this
30867 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30869 // defaults : white / black...
30870 this.applyBlacklists();
30877 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30881 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30887 * @cfg {String} css styling for resizing. (used on bootstrap only)
30891 * @cfg {Number} height (in pixels)
30895 * @cfg {Number} width (in pixels)
30899 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30900 * if you are doing an email editor, this probably needs disabling, it's designed
30905 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30907 enableBlocks : true,
30909 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30912 stylesheets: false,
30914 * @cfg {String} language default en - language of text (usefull for rtl languages)
30920 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30921 * - by default they are stripped - if you are editing email you may need this.
30923 allowComments: false,
30927 // private properties
30928 validationEvent : false,
30930 initialized : false,
30932 sourceEditMode : false,
30933 onFocus : Roo.emptyFn,
30935 hideMode:'offsets',
30939 // blacklist + whitelisted elements..
30946 undoManager : false,
30948 * Protected method that will not generally be called directly. It
30949 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30950 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30952 getDocMarkup : function(){
30956 // inherit styels from page...??
30957 if (this.stylesheets === false) {
30959 Roo.get(document.head).select('style').each(function(node) {
30960 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30963 Roo.get(document.head).select('link').each(function(node) {
30964 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30967 } else if (!this.stylesheets.length) {
30969 st = '<style type="text/css">' +
30970 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30973 for (var i in this.stylesheets) {
30974 if (typeof(this.stylesheets[i]) != 'string') {
30977 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30982 st += '<style type="text/css">' +
30983 'IMG { cursor: pointer } ' +
30986 st += '<meta name="google" content="notranslate">';
30988 var cls = 'notranslate roo-htmleditor-body';
30990 if(this.bodyCls.length){
30991 cls += ' ' + this.bodyCls;
30994 return '<html class="notranslate" translate="no"><head>' + st +
30995 //<style type="text/css">' +
30996 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30998 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
31002 onRender : function(ct, position)
31005 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
31006 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
31009 this.el.dom.style.border = '0 none';
31010 this.el.dom.setAttribute('tabIndex', -1);
31011 this.el.addClass('x-hidden hide');
31015 if(Roo.isIE){ // fix IE 1px bogus margin
31016 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31020 this.frameId = Roo.id();
31024 cls: 'form-control', // bootstrap..
31026 name: this.frameId,
31027 frameBorder : 'no',
31028 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
31031 ifcfg.style = { resize : this.resize };
31034 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
31037 this.iframe = iframe.dom;
31039 this.assignDocWin();
31041 this.doc.designMode = 'on';
31044 this.doc.write(this.getDocMarkup());
31048 var task = { // must defer to wait for browser to be ready
31050 //console.log("run task?" + this.doc.readyState);
31051 this.assignDocWin();
31052 if(this.doc.body || this.doc.readyState == 'complete'){
31054 this.doc.designMode="on";
31059 Roo.TaskMgr.stop(task);
31060 this.initEditor.defer(10, this);
31067 Roo.TaskMgr.start(task);
31072 onResize : function(w, h)
31074 Roo.log('resize: ' +w + ',' + h );
31075 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31079 if(typeof w == 'number'){
31081 this.iframe.style.width = w + 'px';
31083 if(typeof h == 'number'){
31085 this.iframe.style.height = h + 'px';
31087 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31094 * Toggles the editor between standard and source edit mode.
31095 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31097 toggleSourceEdit : function(sourceEditMode){
31099 this.sourceEditMode = sourceEditMode === true;
31101 if(this.sourceEditMode){
31103 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
31106 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31107 //this.iframe.className = '';
31110 //this.setSize(this.owner.wrap.getSize());
31111 //this.fireEvent('editmodechange', this, this.sourceEditMode);
31118 * Protected method that will not generally be called directly. If you need/want
31119 * custom HTML cleanup, this is the method you should override.
31120 * @param {String} html The HTML to be cleaned
31121 * return {String} The cleaned HTML
31123 cleanHtml : function(html)
31125 html = String(html);
31126 if(html.length > 5){
31127 if(Roo.isSafari){ // strip safari nonsense
31128 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31131 if(html == ' '){
31138 * HTML Editor -> Textarea
31139 * Protected method that will not generally be called directly. Syncs the contents
31140 * of the editor iframe with the textarea.
31142 syncValue : function()
31144 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31145 if(this.initialized){
31147 if (this.undoManager) {
31148 this.undoManager.addEvent();
31152 var bd = (this.doc.body || this.doc.documentElement);
31155 var sel = this.win.getSelection();
31157 var div = document.createElement('div');
31158 div.innerHTML = bd.innerHTML;
31159 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31160 if (gtx.length > 0) {
31161 var rm = gtx.item(0).parentNode;
31162 rm.parentNode.removeChild(rm);
31166 if (this.enableBlocks) {
31167 new Roo.htmleditor.FilterBlock({ node : div });
31170 var html = div.innerHTML;
31173 if (this.autoClean) {
31175 new Roo.htmleditor.FilterAttributes({
31197 attrib_clean : ['href', 'src' ]
31200 var tidy = new Roo.htmleditor.TidySerializer({
31203 html = tidy.serialize(div);
31209 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31210 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31212 html = '<div style="'+m[0]+'">' + html + '</div>';
31215 html = this.cleanHtml(html);
31216 // fix up the special chars.. normaly like back quotes in word...
31217 // however we do not want to do this with chinese..
31218 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31220 var cc = match.charCodeAt();
31222 // Get the character value, handling surrogate pairs
31223 if (match.length == 2) {
31224 // It's a surrogate pair, calculate the Unicode code point
31225 var high = match.charCodeAt(0) - 0xD800;
31226 var low = match.charCodeAt(1) - 0xDC00;
31227 cc = (high * 0x400) + low + 0x10000;
31229 (cc >= 0x4E00 && cc < 0xA000 ) ||
31230 (cc >= 0x3400 && cc < 0x4E00 ) ||
31231 (cc >= 0xf900 && cc < 0xfb00 )
31236 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31237 return "&#" + cc + ";";
31244 if(this.owner.fireEvent('beforesync', this, html) !== false){
31245 this.el.dom.value = html;
31246 this.owner.fireEvent('sync', this, html);
31252 * TEXTAREA -> EDITABLE
31253 * Protected method that will not generally be called directly. Pushes the value of the textarea
31254 * into the iframe editor.
31256 pushValue : function()
31258 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31259 if(this.initialized){
31260 var v = this.el.dom.value.trim();
31263 if(this.owner.fireEvent('beforepush', this, v) !== false){
31264 var d = (this.doc.body || this.doc.documentElement);
31267 this.el.dom.value = d.innerHTML;
31268 this.owner.fireEvent('push', this, v);
31270 if (this.autoClean) {
31271 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31272 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31274 if (this.enableBlocks) {
31275 Roo.htmleditor.Block.initAll(this.doc.body);
31278 this.updateLanguage();
31280 var lc = this.doc.body.lastChild;
31281 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31282 // add an extra line at the end.
31283 this.doc.body.appendChild(this.doc.createElement('br'));
31291 deferFocus : function(){
31292 this.focus.defer(10, this);
31296 focus : function(){
31297 if(this.win && !this.sourceEditMode){
31304 assignDocWin: function()
31306 var iframe = this.iframe;
31309 this.doc = iframe.contentWindow.document;
31310 this.win = iframe.contentWindow;
31312 // if (!Roo.get(this.frameId)) {
31315 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31316 // this.win = Roo.get(this.frameId).dom.contentWindow;
31318 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31322 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31323 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31328 initEditor : function(){
31329 //console.log("INIT EDITOR");
31330 this.assignDocWin();
31334 this.doc.designMode="on";
31336 this.doc.write(this.getDocMarkup());
31339 var dbody = (this.doc.body || this.doc.documentElement);
31340 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31341 // this copies styles from the containing element into thsi one..
31342 // not sure why we need all of this..
31343 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31345 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31346 //ss['background-attachment'] = 'fixed'; // w3c
31347 dbody.bgProperties = 'fixed'; // ie
31348 dbody.setAttribute("translate", "no");
31350 //Roo.DomHelper.applyStyles(dbody, ss);
31351 Roo.EventManager.on(this.doc, {
31353 'mouseup': this.onEditorEvent,
31354 'dblclick': this.onEditorEvent,
31355 'click': this.onEditorEvent,
31356 'keyup': this.onEditorEvent,
31361 Roo.EventManager.on(this.doc, {
31362 'paste': this.onPasteEvent,
31366 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31369 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31370 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31372 this.initialized = true;
31375 // initialize special key events - enter
31376 new Roo.htmleditor.KeyEnter({core : this});
31380 this.owner.fireEvent('initialize', this);
31383 // this is to prevent a href clicks resulting in a redirect?
31385 onPasteEvent : function(e,v)
31387 // I think we better assume paste is going to be a dirty load of rubish from word..
31389 // even pasting into a 'email version' of this widget will have to clean up that mess.
31390 var cd = (e.browserEvent.clipboardData || window.clipboardData);
31392 // check what type of paste - if it's an image, then handle it differently.
31393 if (cd.files && cd.files.length > 0 && cd.types.indexOf('text/html') < 0) {
31395 var urlAPI = (window.createObjectURL && window) ||
31396 (window.URL && URL.revokeObjectURL && URL) ||
31397 (window.webkitURL && webkitURL);
31399 var r = new FileReader();
31401 r.addEventListener('load',function()
31404 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31405 // is insert asycn?
31406 if (t.enableBlocks) {
31408 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31409 if (img.closest('figure')) { // assume!! that it's aready
31412 var fig = new Roo.htmleditor.BlockFigure({
31413 image_src : img.src
31415 fig.updateElement(img); // replace it..
31419 t.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31420 t.owner.fireEvent('paste', this);
31422 r.readAsDataURL(cd.files[0]);
31424 e.preventDefault();
31428 if (cd.types.indexOf('text/html') < 0 ) {
31432 var html = cd.getData('text/html'); // clipboard event
31433 if (cd.types.indexOf('text/rtf') > -1) {
31434 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31435 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31440 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31441 .map(function(g) { return g.toDataURL(); })
31442 .filter(function(g) { return g != 'about:blank'; });
31445 html = this.cleanWordChars(html);
31447 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31450 var sn = this.getParentElement();
31451 // check if d contains a table, and prevent nesting??
31452 //Roo.log(d.getElementsByTagName('table'));
31454 //Roo.log(sn.closest('table'));
31455 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31456 e.preventDefault();
31457 this.insertAtCursor("You can not nest tables");
31458 //Roo.log("prevent?"); // fixme -
31464 if (images.length > 0) {
31465 // replace all v:imagedata - with img.
31466 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31467 Roo.each(ar, function(node) {
31468 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31469 node.parentNode.removeChild(node);
31473 Roo.each(d.getElementsByTagName('img'), function(img, i) {
31474 img.setAttribute('src', images[i]);
31477 if (this.autoClean) {
31478 new Roo.htmleditor.FilterWord({ node : d });
31480 new Roo.htmleditor.FilterStyleToTag({ node : d });
31481 new Roo.htmleditor.FilterAttributes({
31483 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31484 attrib_clean : ['href', 'src' ]
31486 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31487 // should be fonts..
31488 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31489 new Roo.htmleditor.FilterParagraph({ node : d });
31490 new Roo.htmleditor.FilterSpan({ node : d });
31491 new Roo.htmleditor.FilterLongBr({ node : d });
31492 new Roo.htmleditor.FilterComment({ node : d });
31496 if (this.enableBlocks) {
31498 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31499 if (img.closest('figure')) { // assume!! that it's aready
31502 var fig = new Roo.htmleditor.BlockFigure({
31503 image_src : img.src
31505 fig.updateElement(img); // replace it..
31511 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31512 if (this.enableBlocks) {
31513 Roo.htmleditor.Block.initAll(this.doc.body);
31517 e.preventDefault();
31518 this.owner.fireEvent('paste', this);
31520 // default behaveiour should be our local cleanup paste? (optional?)
31521 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31522 //this.owner.fireEvent('paste', e, v);
31525 onDestroy : function(){
31531 //for (var i =0; i < this.toolbars.length;i++) {
31532 // // fixme - ask toolbars for heights?
31533 // this.toolbars[i].onDestroy();
31536 //this.wrap.dom.innerHTML = '';
31537 //this.wrap.remove();
31542 onFirstFocus : function(){
31544 this.assignDocWin();
31545 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31547 this.activated = true;
31550 if(Roo.isGecko){ // prevent silly gecko errors
31552 var s = this.win.getSelection();
31553 if(!s.focusNode || s.focusNode.nodeType != 3){
31554 var r = s.getRangeAt(0);
31555 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31560 this.execCmd('useCSS', true);
31561 this.execCmd('styleWithCSS', false);
31564 this.owner.fireEvent('activate', this);
31568 adjustFont: function(btn){
31569 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31570 //if(Roo.isSafari){ // safari
31573 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31574 if(Roo.isSafari){ // safari
31575 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31576 v = (v < 10) ? 10 : v;
31577 v = (v > 48) ? 48 : v;
31578 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31583 v = Math.max(1, v+adjust);
31585 this.execCmd('FontSize', v );
31588 onEditorEvent : function(e)
31592 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31593 return; // we do not handle this.. (undo manager does..)
31595 // clicking a 'block'?
31597 // in theory this detects if the last element is not a br, then we try and do that.
31598 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31600 e.target.nodeName == 'BODY' &&
31601 e.type == "mouseup" &&
31602 this.doc.body.lastChild
31604 var lc = this.doc.body.lastChild;
31605 // gtx-trans is google translate plugin adding crap.
31606 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31607 lc = lc.previousSibling;
31609 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31610 // if last element is <BR> - then dont do anything.
31612 var ns = this.doc.createElement('br');
31613 this.doc.body.appendChild(ns);
31614 range = this.doc.createRange();
31615 range.setStartAfter(ns);
31616 range.collapse(true);
31617 var sel = this.win.getSelection();
31618 sel.removeAllRanges();
31619 sel.addRange(range);
31625 this.fireEditorEvent(e);
31626 // this.updateToolbar();
31627 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31630 fireEditorEvent: function(e)
31632 this.owner.fireEvent('editorevent', this, e);
31635 insertTag : function(tg)
31637 // could be a bit smarter... -> wrap the current selected tRoo..
31638 if (tg.toLowerCase() == 'span' ||
31639 tg.toLowerCase() == 'code' ||
31640 tg.toLowerCase() == 'sup' ||
31641 tg.toLowerCase() == 'sub'
31644 range = this.createRange(this.getSelection());
31645 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31646 wrappingNode.appendChild(range.extractContents());
31647 range.insertNode(wrappingNode);
31654 this.execCmd("formatblock", tg);
31655 this.undoManager.addEvent();
31658 insertText : function(txt)
31662 var range = this.createRange();
31663 range.deleteContents();
31664 //alert(Sender.getAttribute('label'));
31666 range.insertNode(this.doc.createTextNode(txt));
31667 this.undoManager.addEvent();
31673 * Executes a Midas editor command on the editor document and performs necessary focus and
31674 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31675 * @param {String} cmd The Midas command
31676 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31678 relayCmd : function(cmd, value)
31682 case 'justifyleft':
31683 case 'justifyright':
31684 case 'justifycenter':
31685 // if we are in a cell, then we will adjust the
31686 var n = this.getParentElement();
31687 var td = n.closest('td');
31689 var bl = Roo.htmleditor.Block.factory(td);
31690 bl.textAlign = cmd.replace('justify','');
31691 bl.updateElement();
31692 this.owner.fireEvent('editorevent', this);
31695 this.execCmd('styleWithCSS', true); //
31700 // if there is no selection, then we insert, and set the curson inside it..
31701 this.execCmd('styleWithCSS', false);
31711 this.execCmd(cmd, value);
31712 this.owner.fireEvent('editorevent', this);
31713 //this.updateToolbar();
31714 this.owner.deferFocus();
31718 * Executes a Midas editor command directly on the editor document.
31719 * For visual commands, you should use {@link #relayCmd} instead.
31720 * <b>This should only be called after the editor is initialized.</b>
31721 * @param {String} cmd The Midas command
31722 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31724 execCmd : function(cmd, value){
31725 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31732 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31734 * @param {String} text | dom node..
31736 insertAtCursor : function(text)
31739 if(!this.activated){
31743 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31747 // from jquery ui (MIT licenced)
31749 var win = this.win;
31751 if (win.getSelection && win.getSelection().getRangeAt) {
31753 // delete the existing?
31755 this.createRange(this.getSelection()).deleteContents();
31756 range = win.getSelection().getRangeAt(0);
31757 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31758 range.insertNode(node);
31759 range = range.cloneRange();
31760 range.collapse(false);
31762 win.getSelection().removeAllRanges();
31763 win.getSelection().addRange(range);
31767 } else if (win.document.selection && win.document.selection.createRange) {
31768 // no firefox support
31769 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31770 win.document.selection.createRange().pasteHTML(txt);
31773 // no firefox support
31774 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31775 this.execCmd('InsertHTML', txt);
31783 mozKeyPress : function(e){
31785 var c = e.getCharCode(), cmd;
31788 c = String.fromCharCode(c).toLowerCase();
31802 // this.cleanUpPaste.defer(100, this);
31808 this.relayCmd(cmd);
31809 //this.win.focus();
31810 //this.execCmd(cmd);
31811 //this.deferFocus();
31812 e.preventDefault();
31820 fixKeys : function(){ // load time branching for fastest keydown performance
31824 return function(e){
31825 var k = e.getKey(), r;
31828 r = this.doc.selection.createRange();
31831 r.pasteHTML('    ');
31836 /// this is handled by Roo.htmleditor.KeyEnter
31839 r = this.doc.selection.createRange();
31841 var target = r.parentElement();
31842 if(!target || target.tagName.toLowerCase() != 'li'){
31844 r.pasteHTML('<br/>');
31851 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31852 // this.cleanUpPaste.defer(100, this);
31858 }else if(Roo.isOpera){
31859 return function(e){
31860 var k = e.getKey();
31864 this.execCmd('InsertHTML','    ');
31868 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31869 // this.cleanUpPaste.defer(100, this);
31874 }else if(Roo.isSafari){
31875 return function(e){
31876 var k = e.getKey();
31880 this.execCmd('InsertText','\t');
31884 this.mozKeyPress(e);
31886 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31887 // this.cleanUpPaste.defer(100, this);
31895 getAllAncestors: function()
31897 var p = this.getSelectedNode();
31900 a.push(p); // push blank onto stack..
31901 p = this.getParentElement();
31905 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31909 a.push(this.doc.body);
31913 lastSelNode : false,
31916 getSelection : function()
31918 this.assignDocWin();
31919 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31922 * Select a dom node
31923 * @param {DomElement} node the node to select
31925 selectNode : function(node, collapse)
31927 var nodeRange = node.ownerDocument.createRange();
31929 nodeRange.selectNode(node);
31931 nodeRange.selectNodeContents(node);
31933 if (collapse === true) {
31934 nodeRange.collapse(true);
31937 var s = this.win.getSelection();
31938 s.removeAllRanges();
31939 s.addRange(nodeRange);
31942 getSelectedNode: function()
31944 // this may only work on Gecko!!!
31946 // should we cache this!!!!
31950 var range = this.createRange(this.getSelection()).cloneRange();
31953 var parent = range.parentElement();
31955 var testRange = range.duplicate();
31956 testRange.moveToElementText(parent);
31957 if (testRange.inRange(range)) {
31960 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31963 parent = parent.parentElement;
31968 // is ancestor a text element.
31969 var ac = range.commonAncestorContainer;
31970 if (ac.nodeType == 3) {
31971 ac = ac.parentNode;
31974 var ar = ac.childNodes;
31977 var other_nodes = [];
31978 var has_other_nodes = false;
31979 for (var i=0;i<ar.length;i++) {
31980 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31983 // fullly contained node.
31985 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31990 // probably selected..
31991 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31992 other_nodes.push(ar[i]);
31996 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
32001 has_other_nodes = true;
32003 if (!nodes.length && other_nodes.length) {
32004 nodes= other_nodes;
32006 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
32014 createRange: function(sel)
32016 // this has strange effects when using with
32017 // top toolbar - not sure if it's a great idea.
32018 //this.editor.contentWindow.focus();
32019 if (typeof sel != "undefined") {
32021 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32023 return this.doc.createRange();
32026 return this.doc.createRange();
32029 getParentElement: function()
32032 this.assignDocWin();
32033 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32035 var range = this.createRange(sel);
32038 var p = range.commonAncestorContainer;
32039 while (p.nodeType == 3) { // text node
32050 * Range intersection.. the hard stuff...
32054 * [ -- selected range --- ]
32058 * if end is before start or hits it. fail.
32059 * if start is after end or hits it fail.
32061 * if either hits (but other is outside. - then it's not
32067 // @see http://www.thismuchiknow.co.uk/?p=64.
32068 rangeIntersectsNode : function(range, node)
32070 var nodeRange = node.ownerDocument.createRange();
32072 nodeRange.selectNode(node);
32074 nodeRange.selectNodeContents(node);
32077 var rangeStartRange = range.cloneRange();
32078 rangeStartRange.collapse(true);
32080 var rangeEndRange = range.cloneRange();
32081 rangeEndRange.collapse(false);
32083 var nodeStartRange = nodeRange.cloneRange();
32084 nodeStartRange.collapse(true);
32086 var nodeEndRange = nodeRange.cloneRange();
32087 nodeEndRange.collapse(false);
32089 return rangeStartRange.compareBoundaryPoints(
32090 Range.START_TO_START, nodeEndRange) == -1 &&
32091 rangeEndRange.compareBoundaryPoints(
32092 Range.START_TO_START, nodeStartRange) == 1;
32096 rangeCompareNode : function(range, node)
32098 var nodeRange = node.ownerDocument.createRange();
32100 nodeRange.selectNode(node);
32102 nodeRange.selectNodeContents(node);
32106 range.collapse(true);
32108 nodeRange.collapse(true);
32110 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32111 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
32113 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32115 var nodeIsBefore = ss == 1;
32116 var nodeIsAfter = ee == -1;
32118 if (nodeIsBefore && nodeIsAfter) {
32121 if (!nodeIsBefore && nodeIsAfter) {
32122 return 1; //right trailed.
32125 if (nodeIsBefore && !nodeIsAfter) {
32126 return 2; // left trailed.
32132 cleanWordChars : function(input) {// change the chars to hex code
32135 [ 8211, "–" ],
32136 [ 8212, "—" ],
32144 var output = input;
32145 Roo.each(swapCodes, function(sw) {
32146 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32148 output = output.replace(swapper, sw[1]);
32158 cleanUpChild : function (node)
32161 new Roo.htmleditor.FilterComment({node : node});
32162 new Roo.htmleditor.FilterAttributes({
32164 attrib_black : this.ablack,
32165 attrib_clean : this.aclean,
32166 style_white : this.cwhite,
32167 style_black : this.cblack
32169 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32170 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32176 * Clean up MS wordisms...
32177 * @deprecated - use filter directly
32179 cleanWord : function(node)
32181 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32182 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32189 * @deprecated - use filters
32191 cleanTableWidths : function(node)
32193 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32200 applyBlacklists : function()
32202 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
32203 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
32205 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
32206 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
32207 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
32211 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32212 if (b.indexOf(tag) > -1) {
32215 this.white.push(tag);
32219 Roo.each(w, function(tag) {
32220 if (b.indexOf(tag) > -1) {
32223 if (this.white.indexOf(tag) > -1) {
32226 this.white.push(tag);
32231 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32232 if (w.indexOf(tag) > -1) {
32235 this.black.push(tag);
32239 Roo.each(b, function(tag) {
32240 if (w.indexOf(tag) > -1) {
32243 if (this.black.indexOf(tag) > -1) {
32246 this.black.push(tag);
32251 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
32252 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
32256 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32257 if (b.indexOf(tag) > -1) {
32260 this.cwhite.push(tag);
32264 Roo.each(w, function(tag) {
32265 if (b.indexOf(tag) > -1) {
32268 if (this.cwhite.indexOf(tag) > -1) {
32271 this.cwhite.push(tag);
32276 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32277 if (w.indexOf(tag) > -1) {
32280 this.cblack.push(tag);
32284 Roo.each(b, function(tag) {
32285 if (w.indexOf(tag) > -1) {
32288 if (this.cblack.indexOf(tag) > -1) {
32291 this.cblack.push(tag);
32296 setStylesheets : function(stylesheets)
32298 if(typeof(stylesheets) == 'string'){
32299 Roo.get(this.iframe.contentDocument.head).createChild({
32301 rel : 'stylesheet',
32310 Roo.each(stylesheets, function(s) {
32315 Roo.get(_this.iframe.contentDocument.head).createChild({
32317 rel : 'stylesheet',
32327 updateLanguage : function()
32329 if (!this.iframe || !this.iframe.contentDocument) {
32332 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32336 removeStylesheets : function()
32340 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32345 setStyle : function(style)
32347 Roo.get(this.iframe.contentDocument.head).createChild({
32356 // hide stuff that is not compatible
32370 * @event specialkey
32374 * @cfg {String} fieldClass @hide
32377 * @cfg {String} focusClass @hide
32380 * @cfg {String} autoCreate @hide
32383 * @cfg {String} inputType @hide
32386 * @cfg {String} invalidClass @hide
32389 * @cfg {String} invalidText @hide
32392 * @cfg {String} msgFx @hide
32395 * @cfg {String} validateOnBlur @hide
32399 Roo.HtmlEditorCore.white = [
32400 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32402 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
32403 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
32404 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
32405 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
32406 'TABLE', 'UL', 'XMP',
32408 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
32411 'DIR', 'MENU', 'OL', 'UL', 'DL',
32417 Roo.HtmlEditorCore.black = [
32418 // 'embed', 'object', // enable - backend responsiblity to clean thiese
32420 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
32421 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
32422 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
32423 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
32424 //'FONT' // CLEAN LATER..
32425 'COLGROUP', 'COL' // messy tables.
32429 Roo.HtmlEditorCore.clean = [ // ?? needed???
32430 'SCRIPT', 'STYLE', 'TITLE', 'XML'
32432 Roo.HtmlEditorCore.tag_remove = [
32437 Roo.HtmlEditorCore.ablack = [
32441 Roo.HtmlEditorCore.aclean = [
32442 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
32446 Roo.HtmlEditorCore.pwhite= [
32447 'http', 'https', 'mailto'
32450 // white listed style attributes.
32451 Roo.HtmlEditorCore.cwhite= [
32452 // 'text-align', /// default is to allow most things..
32458 // black listed style attributes.
32459 Roo.HtmlEditorCore.cblack= [
32460 // 'font-size' -- this can be set by the project
32474 * @class Roo.bootstrap.form.HtmlEditor
32475 * @extends Roo.bootstrap.form.TextArea
32476 * Bootstrap HtmlEditor class
32479 * Create a new HtmlEditor
32480 * @param {Object} config The config object
32483 Roo.bootstrap.form.HtmlEditor = function(config){
32487 * @event initialize
32488 * Fires when the editor is fully initialized (including the iframe)
32489 * @param {Roo.bootstrap.form.HtmlEditor} this
32494 * Fires when the editor is first receives the focus. Any insertion must wait
32495 * until after this event.
32496 * @param {Roo.bootstrap.form.HtmlEditor} this
32500 * @event beforesync
32501 * Fires before the textarea is updated with content from the editor iframe. Return false
32502 * to cancel the sync.
32503 * @param {Roo.bootstrap.form.HtmlEditor} this
32504 * @param {String} html
32508 * @event beforepush
32509 * Fires before the iframe editor is updated with content from the textarea. Return false
32510 * to cancel the push.
32511 * @param {Roo.bootstrap.form.HtmlEditor} this
32512 * @param {String} html
32517 * Fires when the textarea is updated with content from the editor iframe.
32518 * @param {Roo.bootstrap.form.HtmlEditor} this
32519 * @param {String} html
32524 * Fires when the iframe editor is updated with content from the textarea.
32525 * @param {Roo.bootstrap.form.HtmlEditor} this
32526 * @param {String} html
32530 * @event editmodechange
32531 * Fires when the editor switches edit modes
32532 * @param {Roo.bootstrap.form.HtmlEditor} this
32533 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32535 editmodechange: true,
32537 * @event editorevent
32538 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32539 * @param {Roo.bootstrap.form.HtmlEditor} this
32543 * @event firstfocus
32544 * Fires when on first focus - needed by toolbars..
32545 * @param {Roo.bootstrap.form.HtmlEditor} this
32550 * Auto save the htmlEditor value as a file into Events
32551 * @param {Roo.bootstrap.form.HtmlEditor} this
32555 * @event savedpreview
32556 * preview the saved version of htmlEditor
32557 * @param {Roo.bootstrap.form.HtmlEditor} this
32559 savedpreview: true,
32561 * @event stylesheetsclick
32562 * Fires when press the Sytlesheets button
32563 * @param {Roo.HtmlEditorCore} this
32565 stylesheetsclick: true,
32568 * Fires when press user pastes into the editor
32569 * @param {Roo.HtmlEditorCore} this
32574 * Fires when on any editor when an image is added (excluding paste)
32575 * @param {Roo.bootstrap.form.HtmlEditor} this
32579 * @event imageupdated
32580 * Fires when on any editor when an image is changed (excluding paste)
32581 * @param {Roo.bootstrap.form.HtmlEditor} this
32582 * @param {HTMLElement} img could also be a figure if blocks are enabled
32584 imageupdate: true ,
32586 * @event imagedelete
32587 * Fires when on any editor when an image is deleted
32588 * @param {Roo.bootstrap.form.HtmlEditor} this
32589 * @param {HTMLElement} img could also be a figure if blocks are enabled
32593 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32594 if (!this.toolbars) {
32595 this.toolbars = [];
32598 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32603 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
32607 * @cfg {Array|boolean} toolbars Array of toolbars, or names of toolbars. - true for standard, and false for none.
32612 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32617 * @cfg {String} resize (none|both|horizontal|vertical) - css resize of element
32621 * @cfg {Number} height (in pixels)
32625 * @cfg {Number} width (in pixels)
32630 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32633 stylesheets: false,
32638 // private properties
32639 validationEvent : false,
32641 initialized : false,
32644 onFocus : Roo.emptyFn,
32646 hideMode:'offsets',
32648 tbContainer : false,
32652 toolbarContainer :function() {
32653 return this.wrap.select('.x-html-editor-tb',true).first();
32657 * Protected method that will not generally be called directly. It
32658 * is called when the editor creates its toolbar. Override this method if you need to
32659 * add custom toolbar buttons.
32660 * @param {HtmlEditor} editor
32662 createToolbar : function()
32664 //Roo.log('renewing');
32665 //Roo.log("create toolbars");
32666 if (this.toolbars === false) {
32669 if (this.toolbars === true) {
32670 this.toolbars = [ 'Standard' ];
32673 var ar = Array.from(this.toolbars);
32674 this.toolbars = [];
32675 ar.forEach(function(t,i) {
32676 if (typeof(t) == 'string') {
32681 if (typeof(t) == 'object' && typeof(t.xtype) == 'string') {
32683 t.xns = t.xns || Roo.bootstrap.form.HtmlEditorToolbar;
32684 t = Roo.factory(t);
32686 this.toolbars[i] = t;
32687 this.toolbars[i].render(this.toolbarContainer());
32695 onRender : function(ct, position)
32697 // Roo.log("Call onRender: " + this.xtype);
32699 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32701 this.wrap = this.inputEl().wrap({
32702 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32705 this.editorcore.onRender(ct, position);
32708 this.createToolbar(this);
32716 onResize : function(w, h)
32718 Roo.log('resize: ' +w + ',' + h );
32719 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32723 if(this.inputEl() ){
32724 if(typeof w == 'number'){
32725 var aw = w - this.wrap.getFrameWidth('lr');
32726 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32729 if(typeof h == 'number'){
32730 var tbh = -11; // fixme it needs to tool bar size!
32731 for (var i =0; i < this.toolbars.length;i++) {
32732 // fixme - ask toolbars for heights?
32733 tbh += this.toolbars[i].el.getHeight();
32734 //if (this.toolbars[i].footer) {
32735 // tbh += this.toolbars[i].footer.el.getHeight();
32743 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32744 ah -= 5; // knock a few pixes off for look..
32745 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32749 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32750 this.editorcore.onResize(ew,eh);
32755 * Toggles the editor between standard and source edit mode.
32756 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32758 toggleSourceEdit : function(sourceEditMode)
32760 this.editorcore.toggleSourceEdit(sourceEditMode);
32762 if(this.editorcore.sourceEditMode){
32763 Roo.log('editor - showing textarea');
32766 // Roo.log(this.syncValue());
32768 this.inputEl().removeClass(['hide', 'x-hidden']);
32769 this.inputEl().dom.removeAttribute('tabIndex');
32770 this.inputEl().focus();
32772 Roo.log('editor - hiding textarea');
32774 // Roo.log(this.pushValue());
32777 this.inputEl().addClass(['hide', 'x-hidden']);
32778 this.inputEl().dom.setAttribute('tabIndex', -1);
32779 //this.deferFocus();
32782 //if(this.resizable){
32783 // this.setSize(this.wrap.getSize());
32786 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32789 // private (for BoxComponent)
32790 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32792 // private (for BoxComponent)
32793 getResizeEl : function(){
32797 // private (for BoxComponent)
32798 getPositionEl : function(){
32803 initEvents : function(){
32804 this.originalValue = this.getValue();
32808 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32811 // markInvalid : Roo.emptyFn,
32813 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32816 // clearInvalid : Roo.emptyFn,
32818 setValue : function(v){
32819 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32820 this.editorcore.pushValue();
32825 deferFocus : function(){
32826 this.focus.defer(10, this);
32830 focus : function(){
32831 this.editorcore.focus();
32837 onDestroy : function(){
32843 for (var i =0; i < this.toolbars.length;i++) {
32844 // fixme - ask toolbars for heights?
32845 this.toolbars[i].onDestroy();
32848 this.wrap.dom.innerHTML = '';
32849 this.wrap.remove();
32854 onFirstFocus : function(){
32855 //Roo.log("onFirstFocus");
32856 this.editorcore.onFirstFocus();
32857 for (var i =0; i < this.toolbars.length;i++) {
32858 this.toolbars[i].onFirstFocus();
32864 syncValue : function()
32866 this.editorcore.syncValue();
32869 pushValue : function()
32871 this.editorcore.pushValue();
32875 // hide stuff that is not compatible
32889 * @event specialkey
32893 * @cfg {String} fieldClass @hide
32896 * @cfg {String} focusClass @hide
32899 * @cfg {String} autoCreate @hide
32902 * @cfg {String} inputType @hide
32906 * @cfg {String} invalidText @hide
32909 * @cfg {String} msgFx @hide
32912 * @cfg {String} validateOnBlur @hide
32922 * @class Roo.bootstrap.form.HtmlEditorToolbar.Standard
32923 * @parent Roo.bootstrap.form.HtmlEditor
32924 * @extends Roo.bootstrap.nav.Simplebar
32930 new Roo.bootstrap.form.HtmlEditor({
32933 new Roo.bootstrap.form.HtmlEditorToolbar.Standard({
32934 disable : { fonts: 1 , format: 1, ..., ... , ...],
32940 * @cfg {Object} disable List of elements to disable..
32941 * @cfg {Array} btns List of additional buttons.
32945 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32948 Roo.bootstrap.form.HtmlEditorToolbar.Standard = function(config)
32951 Roo.apply(this, config);
32953 // default disabled, based on 'good practice'..
32954 this.disable = this.disable || {};
32955 Roo.applyIf(this.disable, {
32958 specialElements : true
32960 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.constructor.call(this, config);
32962 this.editor = config.editor;
32963 this.editorcore = config.editor.editorcore;
32965 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.btnid; });
32967 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32968 // dont call parent... till later.
32970 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbar.Standard, Roo.bootstrap.nav.Simplebar, {
32975 editorcore : false,
32980 "h1","h2","h3","h4","h5","h6",
32982 "abbr", "acronym", "address", "cite", "samp", "var",
32989 onRender : function(ct, position)
32991 // Roo.log("Call onRender: " + this.xtype);
32993 Roo.bootstrap.form.HtmlEditorToolbar.Standard.superclass.onRender.call(this, ct, position);
32995 this.el.dom.style.marginBottom = '0';
32997 var editorcore = this.editorcore;
32998 var editor= this.editor;
33001 var btn = function(id, cmd , toggle, handler, html){
33003 var event = toggle ? 'toggle' : 'click';
33008 xns: Roo.bootstrap,
33012 cls : 'roo-html-editor-btn-' + id,
33013 cmd : cmd, // why id || cmd
33014 enableToggle: toggle !== false,
33016 pressed : toggle ? false : null,
33019 a.listeners[toggle ? 'toggle' : 'click'] = function() {
33020 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
33026 // var cb_box = function...
33031 xns: Roo.bootstrap,
33033 cls : 'roo-html-editor-font-chooser',
33037 xns: Roo.bootstrap,
33041 Roo.each(this.formats, function(f) {
33042 style.menu.items.push({
33044 xns: Roo.bootstrap,
33045 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
33050 editorcore.insertTag(this.tagname);
33057 children.push(style);
33059 btn('bold', 'bold',true);
33060 btn('italic', 'italic',true);
33061 btn('underline', 'underline',true);
33062 btn('align-left', 'justifyleft',true);
33063 btn('align-center', 'justifycenter',true);
33064 btn('align-right' , 'justifyright',true);
33065 btn('link', false, true, this.onLinkClick);
33068 btn('image', false, true, this.onImageClick);
33069 btn('list','insertunorderedlist',true);
33070 btn('list-ol','insertorderedlist',true);
33072 btn('pencil', false,true, function(btn){
33074 this.toggleSourceEdit(btn.pressed);
33077 if (this.editor.btns.length > 0) {
33078 for (var i = 0; i<this.editor.btns.length; i++) {
33079 children.push(this.editor.btns[i]);
33085 this.xtype = 'NavSimplebar'; // why?
33087 for(var i=0;i< children.length;i++) {
33089 this.buttons.add(this.addxtypeChild(children[i]));
33092 this.buildToolbarDelete();
33094 editor.on('editorevent', this.updateToolbar, this);
33097 buildToolbarDelete : function()
33100 /* this.addxtypeChild({
33102 xns : Roo.bootstrap,
33103 cls : 'roo-htmleditor-fill'
33106 this.deleteBtn = this.addxtypeChild({
33109 xns: Roo.bootstrap,
33112 click : this.onDelete.createDelegate(this)
33115 this.deleteBtn.hide();
33119 onImageClick : function()
33122 this.input.un('change', this.onFileSelected, this);
33124 this.input = Roo.get(document.body).createChild({
33127 style : 'display:none',
33128 multiple: 'multiple'
33130 this.input.on('change', this.onFileSelected, this);
33131 this.input.dom.click();
33134 onFileSelected : function(e)
33136 e.preventDefault();
33138 if(typeof(this.input.dom.files) == 'undefined' || !this.input.dom.files.length){
33143 this.addFiles(Array.prototype.slice.call(this.input.dom.files), false);
33146 addFiles : function(far, fire_add) {
33149 var editor = this.editorcore;
33153 this.editor.syncValue();
33154 editor.owner.fireEvent('editorevent', editor.owner, false);
33155 editor.owner.fireEvent('imageadd', editor.owner, false);
33162 if (!f.type.match(/^image/)) {
33163 this.addFiles(far, fire_add);
33167 var sn = this.selectedNode;
33169 var bl = sn && this.editorcore.enableBlocks ? Roo.htmleditor.Block.factory(sn) : false;
33172 var reader = new FileReader();
33173 reader.addEventListener('load', (function() {
33175 bl.image_src = reader.result;
33176 //bl.caption = f.name;
33177 bl.updateElement(sn);
33178 this.editor.syncValue();
33179 editor.owner.fireEvent('editorevent', editor.owner, false);
33180 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33181 // we only do the first file!! and replace.
33184 if (this.editorcore.enableBlocks) {
33185 var fig = new Roo.htmleditor.BlockFigure({
33186 image_src : reader.result,
33188 caption_display : 'none' //default to hide captions..
33190 editor.insertAtCursor(fig.toHTML());
33191 this.addFiles(far, true);
33194 // just a standard img..
33195 if (sn && sn.tagName.toUpperCase() == 'IMG') {
33196 sn.src = reader.result;
33197 this.editor.syncValue();
33198 editor.owner.fireEvent('editorevent', editor.owner, false);
33199 editor.owner.fireEvent('imageupdate', editor.owner, sn);
33202 editor.insertAtCursor('<img src="' + reader.result +'">');
33203 this.addFiles(far, true);
33205 }).createDelegate(this));
33206 reader.readAsDataURL(f);
33212 onBtnClick : function(id)
33214 this.editorcore.relayCmd(id);
33215 this.editorcore.focus();
33218 onLinkClick : function(btn) {
33219 var url = this.selectedNode && this.selectedNode.tagName.toUpperCase() == 'A' ?
33220 this.selectedNode.getAttribute('href') : '';
33222 Roo.bootstrap.MessageBox.show({
33223 title : "Add / Edit Link URL",
33224 msg : "Enter the URL for the link",
33225 buttons: Roo.bootstrap.MessageBox.OKCANCEL,
33232 fn: function(pressed, newurl) {
33233 if (pressed != 'ok') {
33234 this.editorcore.focus();
33238 this.selectedNode.setAttribute('href', newurl);
33241 if(newurl && newurl .match(/http(s):\/\/.+/)) {
33242 this.editorcore.relayCmd('createlink', newurl);
33244 this.editorcore.focus();
33249 * Protected method that will not generally be called directly. It triggers
33250 * a toolbar update by reading the markup state of the current selection in the editor.
33252 updateToolbar: function(editor ,ev, sel){
33254 if(!this.editorcore.activated){
33255 this.editor.onFirstFocus(); // is this neeed?
33259 var btns = this.buttons;
33260 var doc = this.editorcore.doc;
33261 var hasToggle = false;
33262 btns.each(function(e) {
33263 if (e.enableToggle && e.cmd) {
33264 hasToggle = hasToggle || (['align-left', 'align-right', 'align-center', 'image' , 'link', 'underline'].indexOf(e.btnid) < 0 && doc.queryCommandState(e.cmd));
33265 e.setActive(doc.queryCommandState(e.cmd));
33271 (ev.type == 'mouseup' || ev.type == 'click' ) &&
33272 ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
33273 // they have click on an image...
33274 // let's see if we can change the selection...
33279 var ans = this.editorcore.getAllAncestors();
33281 sel = ans.length ? (ans[0] ? ans[0] : ans[1]) : this.editorcore.doc.body;
33282 sel = sel ? sel : this.editorcore.doc.body;
33283 sel = sel.tagName.length ? sel : this.editorcore.doc.body;
33287 var lastSel = this.selectedNode;
33288 this.selectedNode = sel;
33290 // ok see if we are editing a block?
33293 // you are not actually selecting the block.
33294 if (sel && sel.hasAttribute('data-block')) {
33296 } else if (sel && sel.closest('[data-block]')) {
33297 db = sel.closest('[data-block]');
33300 Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
33301 e.classList.remove('roo-ed-selection');
33305 if (db && this.editorcore.enableBlocks) {
33306 block = Roo.htmleditor.Block.factory(db);
33309 db.className = (db.classList.length > 0 ? db.className + ' ' : '') +
33310 ' roo-ed-selection';
33311 sel = this.selectedNode = db;
33315 // highlight the 'a'..
33316 var tn = sel && sel.tagName.toUpperCase() || '';
33317 if (!block && sel && tn != 'A') {
33318 var asel = sel.closest('A');
33324 btns.get('link').setActive(tn == 'A' && this.selectedNode.hasAttribute('href'));
33325 btns.get('image').setActive(tn == 'IMG' || this.editorcore.enableBlocks && tn == 'FIGURE');
33326 btns.get('underline').setActive(tn == 'U' || sel.closest('u') ? true : false);
33328 Roo.bootstrap.menu.Manager.hideAll();
33334 // handle delete button..
33335 if (hasToggle || (tn.length && tn == 'BODY')) {
33336 this.deleteBtn.hide();
33340 this.deleteBtn.show();
33344 //this.editorsyncValue();
33346 onFirstFocus: function() {
33347 this.buttons.each(function(item){
33352 onDelete : function()
33354 var range = this.editorcore.createRange();
33355 var selection = this.editorcore.getSelection();
33356 var sn = this.selectedNode;
33357 range.setStart(sn,0);
33358 range.setEnd(sn,0);
33361 if (sn.hasAttribute('data-block')) {
33362 var block = Roo.htmleditor.Block.factory(this.selectedNode);
33364 sn = block.removeNode();
33365 sn.parentNode.removeChild(sn);
33366 selection.removeAllRanges();
33367 selection.addRange(range);
33368 this.updateToolbar(null, null, null);
33369 if (sn.tagName.toUpperCase() == 'FIGURE') {
33370 this.editor.syncValue();
33371 this.editor.fireEvent('imagedelete', this.editor, sn);
33374 this.selectedNode = false;
33375 this.editorcore.fireEditorEvent(false);
33381 return; // should not really happen..
33383 if (sn && sn.tagName == 'BODY') {
33386 var stn = sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
33388 // remove and keep parents.
33389 a = new Roo.htmleditor.FilterKeepChildren({tag : false});
33392 selection.removeAllRanges();
33393 selection.addRange(range);
33394 if (sn.tagName.toUpperCase() == 'IMG"') {
33395 this.editor.syncValue();
33396 this.editor.fireEvent('imagedelete', this.editor, sn);
33399 this.selectedNode = false;
33400 this.editorcore.fireEditorEvent(false);
33406 toggleSourceEdit : function(sourceEditMode){
33409 if(sourceEditMode){
33410 Roo.log("disabling buttons");
33411 this.buttons.each( function(item){
33412 if(item.cmd != 'pencil'){
33418 Roo.log("enabling buttons");
33419 if(this.editorcore.initialized){
33420 this.buttons.each( function(item){
33426 Roo.log("calling toggole on editor");
33427 // tell the editor that it's been pressed..
33428 this.editor.toggleSourceEdit(sourceEditMode);
33442 * @class Roo.bootstrap.form.Markdown
33443 * @extends Roo.bootstrap.form.TextArea
33444 * Bootstrap Showdown editable area
33445 * @cfg {string} content
33448 * Create a new Showdown
33451 Roo.bootstrap.form.Markdown = function(config){
33452 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33456 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
33460 initEvents : function()
33463 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33464 this.markdownEl = this.el.createChild({
33465 cls : 'roo-markdown-area'
33467 this.inputEl().addClass('d-none');
33468 if (this.getValue() == '') {
33469 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33472 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33474 this.markdownEl.on('click', this.toggleTextEdit, this);
33475 this.on('blur', this.toggleTextEdit, this);
33476 this.on('specialkey', this.resizeTextArea, this);
33479 toggleTextEdit : function()
33481 var sh = this.markdownEl.getHeight();
33482 this.inputEl().addClass('d-none');
33483 this.markdownEl.addClass('d-none');
33484 if (!this.editing) {
33486 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33487 this.inputEl().removeClass('d-none');
33488 this.inputEl().focus();
33489 this.editing = true;
33492 // show showdown...
33493 this.updateMarkdown();
33494 this.markdownEl.removeClass('d-none');
33495 this.editing = false;
33498 updateMarkdown : function()
33500 if (this.getValue() == '') {
33501 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33505 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33508 resizeTextArea: function () {
33511 Roo.log([sh, this.getValue().split("\n").length * 30]);
33512 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33514 setValue : function(val)
33516 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33517 if (!this.editing) {
33518 this.updateMarkdown();
33524 if (!this.editing) {
33525 this.toggleTextEdit();
33533 * Ext JS Library 1.1.1
33534 * Copyright(c) 2006-2007, Ext JS, LLC.
33536 * Originally Released Under LGPL - original licence link has changed is not relivant.
33539 * <script type="text/javascript">
33543 * @class Roo.bootstrap.PagingToolbar
33544 * @extends Roo.bootstrap.nav.Simplebar
33545 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33547 * Create a new PagingToolbar
33548 * @param {Object} config The config object
33549 * @param {Roo.data.Store} store
33551 Roo.bootstrap.PagingToolbar = function(config)
33553 // old args format still supported... - xtype is prefered..
33554 // created from xtype...
33556 this.ds = config.dataSource;
33558 if (config.store && !this.ds) {
33559 this.store= Roo.factory(config.store, Roo.data);
33560 this.ds = this.store;
33561 this.ds.xmodule = this.xmodule || false;
33564 this.toolbarItems = [];
33565 if (config.items) {
33566 this.toolbarItems = config.items;
33569 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33574 this.bind(this.ds);
33577 if (Roo.bootstrap.version == 4) {
33578 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33580 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33585 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33587 * @cfg {Roo.bootstrap.Button} buttons[]
33588 * Buttons for the toolbar
33591 * @cfg {Roo.data.Store} store
33592 * The underlying data store providing the paged data
33595 * @cfg {String/HTMLElement/Element} container
33596 * container The id or element that will contain the toolbar
33599 * @cfg {Boolean} displayInfo
33600 * True to display the displayMsg (defaults to false)
33603 * @cfg {Number} pageSize
33604 * The number of records to display per page (defaults to 20)
33608 * @cfg {String} displayMsg
33609 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33611 displayMsg : 'Displaying {0} - {1} of {2}',
33613 * @cfg {String} emptyMsg
33614 * The message to display when no records are found (defaults to "No data to display")
33616 emptyMsg : 'No data to display',
33618 * Customizable piece of the default paging text (defaults to "Page")
33621 beforePageText : "Page",
33623 * Customizable piece of the default paging text (defaults to "of %0")
33626 afterPageText : "of {0}",
33628 * Customizable piece of the default paging text (defaults to "First Page")
33631 firstText : "First Page",
33633 * Customizable piece of the default paging text (defaults to "Previous Page")
33636 prevText : "Previous Page",
33638 * Customizable piece of the default paging text (defaults to "Next Page")
33641 nextText : "Next Page",
33643 * Customizable piece of the default paging text (defaults to "Last Page")
33646 lastText : "Last Page",
33648 * Customizable piece of the default paging text (defaults to "Refresh")
33651 refreshText : "Refresh",
33655 onRender : function(ct, position)
33657 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33658 this.navgroup.parentId = this.id;
33659 this.navgroup.onRender(this.el, null);
33660 // add the buttons to the navgroup
33662 if(this.displayInfo){
33663 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33664 this.displayEl = this.el.select('.x-paging-info', true).first();
33665 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33666 // this.displayEl = navel.el.select('span',true).first();
33672 Roo.each(_this.buttons, function(e){ // this might need to use render????
33673 Roo.factory(e).render(_this.el);
33677 Roo.each(_this.toolbarItems, function(e) {
33678 _this.navgroup.addItem(e);
33682 this.first = this.navgroup.addItem({
33683 tooltip: this.firstText,
33684 cls: "prev btn-outline-secondary",
33685 html : ' <i class="fa fa-step-backward"></i>',
33687 preventDefault: true,
33688 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33691 this.prev = this.navgroup.addItem({
33692 tooltip: this.prevText,
33693 cls: "prev btn-outline-secondary",
33694 html : ' <i class="fa fa-backward"></i>',
33696 preventDefault: true,
33697 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
33699 //this.addSeparator();
33702 var field = this.navgroup.addItem( {
33704 cls : 'x-paging-position btn-outline-secondary',
33706 html : this.beforePageText +
33707 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33708 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
33711 this.field = field.el.select('input', true).first();
33712 this.field.on("keydown", this.onPagingKeydown, this);
33713 this.field.on("focus", function(){this.dom.select();});
33716 this.afterTextEl = field.el.select('.x-paging-after',true).first();
33717 //this.field.setHeight(18);
33718 //this.addSeparator();
33719 this.next = this.navgroup.addItem({
33720 tooltip: this.nextText,
33721 cls: "next btn-outline-secondary",
33722 html : ' <i class="fa fa-forward"></i>',
33724 preventDefault: true,
33725 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
33727 this.last = this.navgroup.addItem({
33728 tooltip: this.lastText,
33729 html : ' <i class="fa fa-step-forward"></i>',
33730 cls: "next btn-outline-secondary",
33732 preventDefault: true,
33733 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
33735 //this.addSeparator();
33736 this.loading = this.navgroup.addItem({
33737 tooltip: this.refreshText,
33738 cls: "btn-outline-secondary",
33739 html : ' <i class="fa fa-refresh"></i>',
33740 preventDefault: true,
33741 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33747 updateInfo : function(){
33748 if(this.displayEl){
33749 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33750 var msg = count == 0 ?
33754 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
33756 this.displayEl.update(msg);
33761 onLoad : function(ds, r, o)
33763 this.cursor = o.params && o.params.start ? o.params.start : 0;
33765 var d = this.getPageData(),
33770 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33771 this.field.dom.value = ap;
33772 this.first.setDisabled(ap == 1);
33773 this.prev.setDisabled(ap == 1);
33774 this.next.setDisabled(ap == ps);
33775 this.last.setDisabled(ap == ps);
33776 this.loading.enable();
33781 getPageData : function(){
33782 var total = this.ds.getTotalCount();
33785 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33786 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33791 onLoadError : function(proxy, o){
33792 this.loading.enable();
33793 if (this.ds.events.loadexception.listeners.length < 2) {
33794 // nothing has been assigned to loadexception except this...
33796 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33802 onPagingKeydown : function(e){
33803 var k = e.getKey();
33804 var d = this.getPageData();
33806 var v = this.field.dom.value, pageNum;
33807 if(!v || isNaN(pageNum = parseInt(v, 10))){
33808 this.field.dom.value = d.activePage;
33811 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33812 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33815 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))
33817 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33818 this.field.dom.value = pageNum;
33819 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33822 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33824 var v = this.field.dom.value, pageNum;
33825 var increment = (e.shiftKey) ? 10 : 1;
33826 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33829 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33830 this.field.dom.value = d.activePage;
33833 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33835 this.field.dom.value = parseInt(v, 10) + increment;
33836 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33837 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33844 beforeLoad : function(){
33846 this.loading.disable();
33851 onClick : function(which){
33860 ds.load({params:{start: 0, limit: this.pageSize}});
33863 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33866 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33869 var total = ds.getTotalCount();
33870 var extra = total % this.pageSize;
33871 var lastStart = extra ? (total - extra) : total-this.pageSize;
33872 ds.load({params:{start: lastStart, limit: this.pageSize}});
33875 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33881 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33882 * @param {Roo.data.Store} store The data store to unbind
33884 unbind : function(ds){
33885 ds.un("beforeload", this.beforeLoad, this);
33886 ds.un("load", this.onLoad, this);
33887 ds.un("loadexception", this.onLoadError, this);
33888 ds.un("remove", this.updateInfo, this);
33889 ds.un("add", this.updateInfo, this);
33890 this.ds = undefined;
33894 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33895 * @param {Roo.data.Store} store The data store to bind
33897 bind : function(ds){
33898 ds.on("beforeload", this.beforeLoad, this);
33899 ds.on("load", this.onLoad, this);
33900 ds.on("loadexception", this.onLoadError, this);
33901 ds.on("remove", this.updateInfo, this);
33902 ds.on("add", this.updateInfo, this);
33913 * @class Roo.bootstrap.MessageBar
33914 * @extends Roo.bootstrap.Component
33915 * Bootstrap MessageBar class
33916 * @cfg {String} html contents of the MessageBar
33917 * @cfg {String} weight (info | success | warning | danger) default info
33918 * @cfg {String} beforeClass insert the bar before the given class
33919 * @cfg {Boolean} closable (true | false) default false
33920 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33923 * Create a new Element
33924 * @param {Object} config The config object
33927 Roo.bootstrap.MessageBar = function(config){
33928 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33931 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33937 beforeClass: 'bootstrap-sticky-wrap',
33939 getAutoCreate : function(){
33943 cls: 'alert alert-dismissable alert-' + this.weight,
33948 html: this.html || ''
33954 cfg.cls += ' alert-messages-fixed';
33968 onRender : function(ct, position)
33970 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33973 var cfg = Roo.apply({}, this.getAutoCreate());
33977 cfg.cls += ' ' + this.cls;
33980 cfg.style = this.style;
33982 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33984 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33987 this.el.select('>button.close').on('click', this.hide, this);
33993 if (!this.rendered) {
33999 this.fireEvent('show', this);
34005 if (!this.rendered) {
34011 this.fireEvent('hide', this);
34014 update : function()
34016 // var e = this.el.dom.firstChild;
34018 // if(this.closable){
34019 // e = e.nextSibling;
34022 // e.data = this.html || '';
34024 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
34040 * @class Roo.bootstrap.Graph
34041 * @extends Roo.bootstrap.Component
34042 * Bootstrap Graph class
34046 @cfg {String} graphtype bar | vbar | pie
34047 @cfg {number} g_x coodinator | centre x (pie)
34048 @cfg {number} g_y coodinator | centre y (pie)
34049 @cfg {number} g_r radius (pie)
34050 @cfg {number} g_height height of the chart (respected by all elements in the set)
34051 @cfg {number} g_width width of the chart (respected by all elements in the set)
34052 @cfg {Object} title The title of the chart
34055 -opts (object) options for the chart
34057 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
34058 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
34060 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.
34061 o stacked (boolean) whether or not to tread values as in a stacked bar chart
34063 o stretch (boolean)
34065 -opts (object) options for the pie
34068 o startAngle (number)
34069 o endAngle (number)
34073 * Create a new Input
34074 * @param {Object} config The config object
34077 Roo.bootstrap.Graph = function(config){
34078 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
34084 * The img click event for the img.
34085 * @param {Roo.EventObject} e
34091 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
34102 //g_colors: this.colors,
34109 getAutoCreate : function(){
34120 onRender : function(ct,position){
34123 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
34125 if (typeof(Raphael) == 'undefined') {
34126 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
34130 this.raphael = Raphael(this.el.dom);
34132 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34133 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34134 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
34135 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
34137 r.text(160, 10, "Single Series Chart").attr(txtattr);
34138 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
34139 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
34140 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
34142 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
34143 r.barchart(330, 10, 300, 220, data1);
34144 r.barchart(10, 250, 300, 220, data2, {stacked: true});
34145 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
34148 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34149 // r.barchart(30, 30, 560, 250, xdata, {
34150 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
34151 // axis : "0 0 1 1",
34152 // axisxlabels : xdata
34153 // //yvalues : cols,
34156 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
34158 // this.load(null,xdata,{
34159 // axis : "0 0 1 1",
34160 // axisxlabels : xdata
34165 load : function(graphtype,xdata,opts)
34167 this.raphael.clear();
34169 graphtype = this.graphtype;
34174 var r = this.raphael,
34175 fin = function () {
34176 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
34178 fout = function () {
34179 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
34181 pfin = function() {
34182 this.sector.stop();
34183 this.sector.scale(1.1, 1.1, this.cx, this.cy);
34186 this.label[0].stop();
34187 this.label[0].attr({ r: 7.5 });
34188 this.label[1].attr({ "font-weight": 800 });
34191 pfout = function() {
34192 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
34195 this.label[0].animate({ r: 5 }, 500, "bounce");
34196 this.label[1].attr({ "font-weight": 400 });
34202 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34205 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
34208 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
34209 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
34211 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
34218 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
34223 setTitle: function(o)
34228 initEvents: function() {
34231 this.el.on('click', this.onClick, this);
34235 onClick : function(e)
34237 Roo.log('img onclick');
34238 this.fireEvent('click', this, e);
34244 Roo.bootstrap.dash = {};/*
34250 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34253 * @class Roo.bootstrap.dash.NumberBox
34254 * @extends Roo.bootstrap.Component
34255 * Bootstrap NumberBox class
34256 * @cfg {String} headline Box headline
34257 * @cfg {String} content Box content
34258 * @cfg {String} icon Box icon
34259 * @cfg {String} footer Footer text
34260 * @cfg {String} fhref Footer href
34263 * Create a new NumberBox
34264 * @param {Object} config The config object
34268 Roo.bootstrap.dash.NumberBox = function(config){
34269 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
34273 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
34282 getAutoCreate : function(){
34286 cls : 'small-box ',
34294 cls : 'roo-headline',
34295 html : this.headline
34299 cls : 'roo-content',
34300 html : this.content
34314 cls : 'ion ' + this.icon
34323 cls : 'small-box-footer',
34324 href : this.fhref || '#',
34328 cfg.cn.push(footer);
34335 onRender : function(ct,position){
34336 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34343 setHeadline: function (value)
34345 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34348 setFooter: function (value, href)
34350 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34353 this.el.select('a.small-box-footer',true).first().attr('href', href);
34358 setContent: function (value)
34360 this.el.select('.roo-content',true).first().dom.innerHTML = value;
34363 initEvents: function()
34377 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34380 * @class Roo.bootstrap.dash.TabBox
34381 * @extends Roo.bootstrap.Component
34382 * @children Roo.bootstrap.dash.TabPane
34383 * Bootstrap TabBox class
34384 * @cfg {String} title Title of the TabBox
34385 * @cfg {String} icon Icon of the TabBox
34386 * @cfg {Boolean} showtabs (true|false) show the tabs default true
34387 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34390 * Create a new TabBox
34391 * @param {Object} config The config object
34395 Roo.bootstrap.dash.TabBox = function(config){
34396 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34401 * When a pane is added
34402 * @param {Roo.bootstrap.dash.TabPane} pane
34406 * @event activatepane
34407 * When a pane is activated
34408 * @param {Roo.bootstrap.dash.TabPane} pane
34410 "activatepane" : true
34418 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
34423 tabScrollable : false,
34425 getChildContainer : function()
34427 return this.el.select('.tab-content', true).first();
34430 getAutoCreate : function(){
34434 cls: 'pull-left header',
34442 cls: 'fa ' + this.icon
34448 cls: 'nav nav-tabs pull-right',
34454 if(this.tabScrollable){
34461 cls: 'nav nav-tabs pull-right',
34472 cls: 'nav-tabs-custom',
34477 cls: 'tab-content no-padding',
34485 initEvents : function()
34487 //Roo.log('add add pane handler');
34488 this.on('addpane', this.onAddPane, this);
34491 * Updates the box title
34492 * @param {String} html to set the title to.
34494 setTitle : function(value)
34496 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34498 onAddPane : function(pane)
34500 this.panes.push(pane);
34501 //Roo.log('addpane');
34503 // tabs are rendere left to right..
34504 if(!this.showtabs){
34508 var ctr = this.el.select('.nav-tabs', true).first();
34511 var existing = ctr.select('.nav-tab',true);
34512 var qty = existing.getCount();;
34515 var tab = ctr.createChild({
34517 cls : 'nav-tab' + (qty ? '' : ' active'),
34525 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34528 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34530 pane.el.addClass('active');
34535 onTabClick : function(ev,un,ob,pane)
34537 //Roo.log('tab - prev default');
34538 ev.preventDefault();
34541 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34542 pane.tab.addClass('active');
34543 //Roo.log(pane.title);
34544 this.getChildContainer().select('.tab-pane',true).removeClass('active');
34545 // technically we should have a deactivate event.. but maybe add later.
34546 // and it should not de-activate the selected tab...
34547 this.fireEvent('activatepane', pane);
34548 pane.el.addClass('active');
34549 pane.fireEvent('activate');
34554 getActivePane : function()
34557 Roo.each(this.panes, function(p) {
34558 if(p.el.hasClass('active')){
34579 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34581 * @class Roo.bootstrap.TabPane
34582 * @extends Roo.bootstrap.Component
34583 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
34584 * Bootstrap TabPane class
34585 * @cfg {Boolean} active (false | true) Default false
34586 * @cfg {String} title title of panel
34590 * Create a new TabPane
34591 * @param {Object} config The config object
34594 Roo.bootstrap.dash.TabPane = function(config){
34595 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34601 * When a pane is activated
34602 * @param {Roo.bootstrap.dash.TabPane} pane
34609 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
34614 // the tabBox that this is attached to.
34617 getAutoCreate : function()
34625 cfg.cls += ' active';
34630 initEvents : function()
34632 //Roo.log('trigger add pane handler');
34633 this.parent().fireEvent('addpane', this)
34637 * Updates the tab title
34638 * @param {String} html to set the title to.
34640 setTitle: function(str)
34646 this.tab.select('a', true).first().dom.innerHTML = str;
34665 * @class Roo.bootstrap.Tooltip
34666 * Bootstrap Tooltip class
34667 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34668 * to determine which dom element triggers the tooltip.
34670 * It needs to add support for additional attributes like tooltip-position
34673 * Create a new Toolti
34674 * @param {Object} config The config object
34677 Roo.bootstrap.Tooltip = function(config){
34678 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34680 this.alignment = Roo.bootstrap.Tooltip.alignment;
34682 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34683 this.alignment = config.alignment;
34688 Roo.apply(Roo.bootstrap.Tooltip, {
34690 * @function init initialize tooltip monitoring.
34694 currentTip : false,
34695 currentRegion : false,
34701 Roo.get(document).on('mouseover', this.enter ,this);
34702 Roo.get(document).on('mouseout', this.leave, this);
34705 this.currentTip = new Roo.bootstrap.Tooltip();
34708 enter : function(ev)
34710 var dom = ev.getTarget();
34712 //Roo.log(['enter',dom]);
34713 var el = Roo.fly(dom);
34714 if (this.currentEl) {
34716 //Roo.log(this.currentEl);
34717 //Roo.log(this.currentEl.contains(dom));
34718 if (this.currentEl == el) {
34721 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34727 if (this.currentTip.el) {
34728 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34732 if(!el || el.dom == document){
34738 if (!el.attr('tooltip')) {
34739 pel = el.findParent("[tooltip]");
34741 bindEl = Roo.get(pel);
34747 // you can not look for children, as if el is the body.. then everythign is the child..
34748 if (!pel && !el.attr('tooltip')) { //
34749 if (!el.select("[tooltip]").elements.length) {
34752 // is the mouse over this child...?
34753 bindEl = el.select("[tooltip]").first();
34754 var xy = ev.getXY();
34755 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34756 //Roo.log("not in region.");
34759 //Roo.log("child element over..");
34762 this.currentEl = el;
34763 this.currentTip.bind(bindEl);
34764 this.currentRegion = Roo.lib.Region.getRegion(dom);
34765 this.currentTip.enter();
34768 leave : function(ev)
34770 var dom = ev.getTarget();
34771 //Roo.log(['leave',dom]);
34772 if (!this.currentEl) {
34777 if (dom != this.currentEl.dom) {
34780 var xy = ev.getXY();
34781 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
34784 // only activate leave if mouse cursor is outside... bounding box..
34789 if (this.currentTip) {
34790 this.currentTip.leave();
34792 //Roo.log('clear currentEl');
34793 this.currentEl = false;
34798 'left' : ['r-l', [-2,0], 'right'],
34799 'right' : ['l-r', [2,0], 'left'],
34800 'bottom' : ['t-b', [0,2], 'top'],
34801 'top' : [ 'b-t', [0,-2], 'bottom']
34807 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
34812 delay : null, // can be { show : 300 , hide: 500}
34816 hoverState : null, //???
34818 placement : 'bottom',
34822 getAutoCreate : function(){
34829 cls : 'tooltip-arrow arrow'
34832 cls : 'tooltip-inner'
34839 bind : function(el)
34844 initEvents : function()
34846 this.arrowEl = this.el.select('.arrow', true).first();
34847 this.innerEl = this.el.select('.tooltip-inner', true).first();
34850 enter : function () {
34852 if (this.timeout != null) {
34853 clearTimeout(this.timeout);
34856 this.hoverState = 'in';
34857 //Roo.log("enter - show");
34858 if (!this.delay || !this.delay.show) {
34863 this.timeout = setTimeout(function () {
34864 if (_t.hoverState == 'in') {
34867 }, this.delay.show);
34871 clearTimeout(this.timeout);
34873 this.hoverState = 'out';
34874 if (!this.delay || !this.delay.hide) {
34880 this.timeout = setTimeout(function () {
34881 //Roo.log("leave - timeout");
34883 if (_t.hoverState == 'out') {
34885 Roo.bootstrap.Tooltip.currentEl = false;
34890 show : function (msg)
34893 this.render(document.body);
34896 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34898 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34900 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34902 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34903 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34905 if(this.bindEl.attr('tooltip-class')) {
34906 this.el.addClass(this.bindEl.attr('tooltip-class'));
34909 var placement = typeof this.placement == 'function' ?
34910 this.placement.call(this, this.el, on_el) :
34913 if(this.bindEl.attr('tooltip-placement')) {
34914 placement = this.bindEl.attr('tooltip-placement');
34917 var autoToken = /\s?auto?\s?/i;
34918 var autoPlace = autoToken.test(placement);
34920 placement = placement.replace(autoToken, '') || 'top';
34924 //this.el.setXY([0,0]);
34926 //this.el.dom.style.display='block';
34928 //this.el.appendTo(on_el);
34930 var p = this.getPosition();
34931 var box = this.el.getBox();
34937 var align = this.alignment[placement];
34939 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34941 if(placement == 'top' || placement == 'bottom'){
34943 placement = 'right';
34946 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34947 placement = 'left';
34950 var scroll = Roo.select('body', true).first().getScroll();
34952 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34956 align = this.alignment[placement];
34958 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34962 var elems = document.getElementsByTagName('div');
34963 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34964 for (var i = 0; i < elems.length; i++) {
34965 var zindex = Number.parseInt(
34966 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34969 if (zindex > highest) {
34976 this.el.dom.style.zIndex = highest;
34978 this.el.alignTo(this.bindEl, align[0],align[1]);
34979 //var arrow = this.el.select('.arrow',true).first();
34980 //arrow.set(align[2],
34982 this.el.addClass(placement);
34983 this.el.addClass("bs-tooltip-"+ placement);
34985 this.el.addClass('in fade show');
34987 this.hoverState = null;
34989 if (this.el.hasClass('fade')) {
35004 //this.el.setXY([0,0]);
35005 if(this.bindEl.attr('tooltip-class')) {
35006 this.el.removeClass(this.bindEl.attr('tooltip-class'));
35008 this.el.removeClass(['show', 'in']);
35024 * @class Roo.bootstrap.LocationPicker
35025 * @extends Roo.bootstrap.Component
35026 * Bootstrap LocationPicker class
35027 * @cfg {Number} latitude Position when init default 0
35028 * @cfg {Number} longitude Position when init default 0
35029 * @cfg {Number} zoom default 15
35030 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
35031 * @cfg {Boolean} mapTypeControl default false
35032 * @cfg {Boolean} disableDoubleClickZoom default false
35033 * @cfg {Boolean} scrollwheel default true
35034 * @cfg {Boolean} streetViewControl default false
35035 * @cfg {Number} radius default 0
35036 * @cfg {String} locationName
35037 * @cfg {Boolean} draggable default true
35038 * @cfg {Boolean} enableAutocomplete default false
35039 * @cfg {Boolean} enableReverseGeocode default true
35040 * @cfg {String} markerTitle
35043 * Create a new LocationPicker
35044 * @param {Object} config The config object
35048 Roo.bootstrap.LocationPicker = function(config){
35050 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
35055 * Fires when the picker initialized.
35056 * @param {Roo.bootstrap.LocationPicker} this
35057 * @param {Google Location} location
35061 * @event positionchanged
35062 * Fires when the picker position changed.
35063 * @param {Roo.bootstrap.LocationPicker} this
35064 * @param {Google Location} location
35066 positionchanged : true,
35069 * Fires when the map resize.
35070 * @param {Roo.bootstrap.LocationPicker} this
35075 * Fires when the map show.
35076 * @param {Roo.bootstrap.LocationPicker} this
35081 * Fires when the map hide.
35082 * @param {Roo.bootstrap.LocationPicker} this
35087 * Fires when click the map.
35088 * @param {Roo.bootstrap.LocationPicker} this
35089 * @param {Map event} e
35093 * @event mapRightClick
35094 * Fires when right click the map.
35095 * @param {Roo.bootstrap.LocationPicker} this
35096 * @param {Map event} e
35098 mapRightClick : true,
35100 * @event markerClick
35101 * Fires when click the marker.
35102 * @param {Roo.bootstrap.LocationPicker} this
35103 * @param {Map event} e
35105 markerClick : true,
35107 * @event markerRightClick
35108 * Fires when right click the marker.
35109 * @param {Roo.bootstrap.LocationPicker} this
35110 * @param {Map event} e
35112 markerRightClick : true,
35114 * @event OverlayViewDraw
35115 * Fires when OverlayView Draw
35116 * @param {Roo.bootstrap.LocationPicker} this
35118 OverlayViewDraw : true,
35120 * @event OverlayViewOnAdd
35121 * Fires when OverlayView Draw
35122 * @param {Roo.bootstrap.LocationPicker} this
35124 OverlayViewOnAdd : true,
35126 * @event OverlayViewOnRemove
35127 * Fires when OverlayView Draw
35128 * @param {Roo.bootstrap.LocationPicker} this
35130 OverlayViewOnRemove : true,
35132 * @event OverlayViewShow
35133 * Fires when OverlayView Draw
35134 * @param {Roo.bootstrap.LocationPicker} this
35135 * @param {Pixel} cpx
35137 OverlayViewShow : true,
35139 * @event OverlayViewHide
35140 * Fires when OverlayView Draw
35141 * @param {Roo.bootstrap.LocationPicker} this
35143 OverlayViewHide : true,
35145 * @event loadexception
35146 * Fires when load google lib failed.
35147 * @param {Roo.bootstrap.LocationPicker} this
35149 loadexception : true
35154 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
35156 gMapContext: false,
35162 mapTypeControl: false,
35163 disableDoubleClickZoom: false,
35165 streetViewControl: false,
35169 enableAutocomplete: false,
35170 enableReverseGeocode: true,
35173 getAutoCreate: function()
35178 cls: 'roo-location-picker'
35184 initEvents: function(ct, position)
35186 if(!this.el.getWidth() || this.isApplied()){
35190 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35195 initial: function()
35197 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
35198 this.fireEvent('loadexception', this);
35202 if(!this.mapTypeId){
35203 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
35206 this.gMapContext = this.GMapContext();
35208 this.initOverlayView();
35210 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
35214 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
35215 _this.setPosition(_this.gMapContext.marker.position);
35218 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
35219 _this.fireEvent('mapClick', this, event);
35223 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
35224 _this.fireEvent('mapRightClick', this, event);
35228 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
35229 _this.fireEvent('markerClick', this, event);
35233 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
35234 _this.fireEvent('markerRightClick', this, event);
35238 this.setPosition(this.gMapContext.location);
35240 this.fireEvent('initial', this, this.gMapContext.location);
35243 initOverlayView: function()
35247 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
35251 _this.fireEvent('OverlayViewDraw', _this);
35256 _this.fireEvent('OverlayViewOnAdd', _this);
35259 onRemove: function()
35261 _this.fireEvent('OverlayViewOnRemove', _this);
35264 show: function(cpx)
35266 _this.fireEvent('OverlayViewShow', _this, cpx);
35271 _this.fireEvent('OverlayViewHide', _this);
35277 fromLatLngToContainerPixel: function(event)
35279 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
35282 isApplied: function()
35284 return this.getGmapContext() == false ? false : true;
35287 getGmapContext: function()
35289 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35292 GMapContext: function()
35294 var position = new google.maps.LatLng(this.latitude, this.longitude);
35296 var _map = new google.maps.Map(this.el.dom, {
35299 mapTypeId: this.mapTypeId,
35300 mapTypeControl: this.mapTypeControl,
35301 disableDoubleClickZoom: this.disableDoubleClickZoom,
35302 scrollwheel: this.scrollwheel,
35303 streetViewControl: this.streetViewControl,
35304 locationName: this.locationName,
35305 draggable: this.draggable,
35306 enableAutocomplete: this.enableAutocomplete,
35307 enableReverseGeocode: this.enableReverseGeocode
35310 var _marker = new google.maps.Marker({
35311 position: position,
35313 title: this.markerTitle,
35314 draggable: this.draggable
35321 location: position,
35322 radius: this.radius,
35323 locationName: this.locationName,
35324 addressComponents: {
35325 formatted_address: null,
35326 addressLine1: null,
35327 addressLine2: null,
35329 streetNumber: null,
35333 stateOrProvince: null
35336 domContainer: this.el.dom,
35337 geodecoder: new google.maps.Geocoder()
35341 drawCircle: function(center, radius, options)
35343 if (this.gMapContext.circle != null) {
35344 this.gMapContext.circle.setMap(null);
35348 options = Roo.apply({}, options, {
35349 strokeColor: "#0000FF",
35350 strokeOpacity: .35,
35352 fillColor: "#0000FF",
35356 options.map = this.gMapContext.map;
35357 options.radius = radius;
35358 options.center = center;
35359 this.gMapContext.circle = new google.maps.Circle(options);
35360 return this.gMapContext.circle;
35366 setPosition: function(location)
35368 this.gMapContext.location = location;
35369 this.gMapContext.marker.setPosition(location);
35370 this.gMapContext.map.panTo(location);
35371 this.drawCircle(location, this.gMapContext.radius, {});
35375 if (this.gMapContext.settings.enableReverseGeocode) {
35376 this.gMapContext.geodecoder.geocode({
35377 latLng: this.gMapContext.location
35378 }, function(results, status) {
35380 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35381 _this.gMapContext.locationName = results[0].formatted_address;
35382 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35384 _this.fireEvent('positionchanged', this, location);
35391 this.fireEvent('positionchanged', this, location);
35396 google.maps.event.trigger(this.gMapContext.map, "resize");
35398 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35400 this.fireEvent('resize', this);
35403 setPositionByLatLng: function(latitude, longitude)
35405 this.setPosition(new google.maps.LatLng(latitude, longitude));
35408 getCurrentPosition: function()
35411 latitude: this.gMapContext.location.lat(),
35412 longitude: this.gMapContext.location.lng()
35416 getAddressName: function()
35418 return this.gMapContext.locationName;
35421 getAddressComponents: function()
35423 return this.gMapContext.addressComponents;
35426 address_component_from_google_geocode: function(address_components)
35430 for (var i = 0; i < address_components.length; i++) {
35431 var component = address_components[i];
35432 if (component.types.indexOf("postal_code") >= 0) {
35433 result.postalCode = component.short_name;
35434 } else if (component.types.indexOf("street_number") >= 0) {
35435 result.streetNumber = component.short_name;
35436 } else if (component.types.indexOf("route") >= 0) {
35437 result.streetName = component.short_name;
35438 } else if (component.types.indexOf("neighborhood") >= 0) {
35439 result.city = component.short_name;
35440 } else if (component.types.indexOf("locality") >= 0) {
35441 result.city = component.short_name;
35442 } else if (component.types.indexOf("sublocality") >= 0) {
35443 result.district = component.short_name;
35444 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35445 result.stateOrProvince = component.short_name;
35446 } else if (component.types.indexOf("country") >= 0) {
35447 result.country = component.short_name;
35451 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35452 result.addressLine2 = "";
35456 setZoomLevel: function(zoom)
35458 this.gMapContext.map.setZoom(zoom);
35471 this.fireEvent('show', this);
35482 this.fireEvent('hide', this);
35487 Roo.apply(Roo.bootstrap.LocationPicker, {
35489 OverlayView : function(map, options)
35491 options = options || {};
35498 * @class Roo.bootstrap.Alert
35499 * @extends Roo.bootstrap.Component
35500 * Bootstrap Alert class - shows an alert area box
35502 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35503 Enter a valid email address
35506 * @cfg {String} title The title of alert
35507 * @cfg {String} html The content of alert
35508 * @cfg {String} weight (success|info|warning|danger) Weight of the message
35509 * @cfg {String} fa font-awesomeicon
35510 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35511 * @cfg {Boolean} close true to show a x closer
35515 * Create a new alert
35516 * @param {Object} config The config object
35520 Roo.bootstrap.Alert = function(config){
35521 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35525 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
35531 faicon: false, // BC
35535 getAutoCreate : function()
35547 style : this.close ? '' : 'display:none'
35551 cls : 'roo-alert-icon'
35556 cls : 'roo-alert-title',
35561 cls : 'roo-alert-text',
35568 cfg.cn[0].cls += ' fa ' + this.faicon;
35571 cfg.cn[0].cls += ' fa ' + this.fa;
35575 cfg.cls += ' alert-' + this.weight;
35581 initEvents: function()
35583 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35584 this.titleEl = this.el.select('.roo-alert-title',true).first();
35585 this.iconEl = this.el.select('.roo-alert-icon',true).first();
35586 this.htmlEl = this.el.select('.roo-alert-text',true).first();
35587 if (this.seconds > 0) {
35588 this.hide.defer(this.seconds, this);
35592 * Set the Title Message HTML
35593 * @param {String} html
35595 setTitle : function(str)
35597 this.titleEl.dom.innerHTML = str;
35601 * Set the Body Message HTML
35602 * @param {String} html
35604 setHtml : function(str)
35606 this.htmlEl.dom.innerHTML = str;
35609 * Set the Weight of the alert
35610 * @param {String} (success|info|warning|danger) weight
35613 setWeight : function(weight)
35616 this.el.removeClass('alert-' + this.weight);
35619 this.weight = weight;
35621 this.el.addClass('alert-' + this.weight);
35624 * Set the Icon of the alert
35625 * @param {String} see fontawsome names (name without the 'fa-' bit)
35627 setIcon : function(icon)
35630 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35633 this.faicon = icon;
35635 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35660 * @class Roo.bootstrap.UploadCropbox
35661 * @extends Roo.bootstrap.Component
35662 * Bootstrap UploadCropbox class
35663 * @cfg {String} emptyText show when image has been loaded
35664 * @cfg {String} rotateNotify show when image too small to rotate
35665 * @cfg {Number} errorTimeout default 3000
35666 * @cfg {Number} minWidth default 300
35667 * @cfg {Number} minHeight default 300
35668 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35669 * @cfg {Boolean} isDocument (true|false) default false
35670 * @cfg {String} url action url
35671 * @cfg {String} paramName default 'imageUpload'
35672 * @cfg {String} method default POST
35673 * @cfg {Boolean} loadMask (true|false) default true
35674 * @cfg {Boolean} loadingText default 'Loading...'
35677 * Create a new UploadCropbox
35678 * @param {Object} config The config object
35681 Roo.bootstrap.UploadCropbox = function(config){
35682 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35686 * @event beforeselectfile
35687 * Fire before select file
35688 * @param {Roo.bootstrap.UploadCropbox} this
35690 "beforeselectfile" : true,
35693 * Fire after initEvent
35694 * @param {Roo.bootstrap.UploadCropbox} this
35699 * Fire after initEvent
35700 * @param {Roo.bootstrap.UploadCropbox} this
35701 * @param {String} data
35706 * Fire when preparing the file data
35707 * @param {Roo.bootstrap.UploadCropbox} this
35708 * @param {Object} file
35713 * Fire when get exception
35714 * @param {Roo.bootstrap.UploadCropbox} this
35715 * @param {XMLHttpRequest} xhr
35717 "exception" : true,
35719 * @event beforeloadcanvas
35720 * Fire before load the canvas
35721 * @param {Roo.bootstrap.UploadCropbox} this
35722 * @param {String} src
35724 "beforeloadcanvas" : true,
35727 * Fire when trash image
35728 * @param {Roo.bootstrap.UploadCropbox} this
35733 * Fire when download the image
35734 * @param {Roo.bootstrap.UploadCropbox} this
35738 * @event footerbuttonclick
35739 * Fire when footerbuttonclick
35740 * @param {Roo.bootstrap.UploadCropbox} this
35741 * @param {String} type
35743 "footerbuttonclick" : true,
35747 * @param {Roo.bootstrap.UploadCropbox} this
35752 * Fire when rotate the image
35753 * @param {Roo.bootstrap.UploadCropbox} this
35754 * @param {String} pos
35759 * Fire when inspect the file
35760 * @param {Roo.bootstrap.UploadCropbox} this
35761 * @param {Object} file
35766 * Fire when xhr upload the file
35767 * @param {Roo.bootstrap.UploadCropbox} this
35768 * @param {Object} data
35773 * Fire when arrange the file data
35774 * @param {Roo.bootstrap.UploadCropbox} this
35775 * @param {Object} formData
35780 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35783 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
35785 emptyText : 'Click to upload image',
35786 rotateNotify : 'Image is too small to rotate',
35787 errorTimeout : 3000,
35801 cropType : 'image/jpeg',
35803 canvasLoaded : false,
35804 isDocument : false,
35806 paramName : 'imageUpload',
35808 loadingText : 'Loading...',
35811 getAutoCreate : function()
35815 cls : 'roo-upload-cropbox',
35819 cls : 'roo-upload-cropbox-selector',
35824 cls : 'roo-upload-cropbox-body',
35825 style : 'cursor:pointer',
35829 cls : 'roo-upload-cropbox-preview'
35833 cls : 'roo-upload-cropbox-thumb'
35837 cls : 'roo-upload-cropbox-empty-notify',
35838 html : this.emptyText
35842 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35843 html : this.rotateNotify
35849 cls : 'roo-upload-cropbox-footer',
35852 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35862 onRender : function(ct, position)
35864 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35866 if (this.buttons.length) {
35868 Roo.each(this.buttons, function(bb) {
35870 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35872 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35878 this.maskEl = this.el;
35882 initEvents : function()
35884 this.urlAPI = (window.createObjectURL && window) ||
35885 (window.URL && URL.revokeObjectURL && URL) ||
35886 (window.webkitURL && webkitURL);
35888 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35889 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35891 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35892 this.selectorEl.hide();
35894 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35895 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35897 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35898 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35899 this.thumbEl.hide();
35901 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35902 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35904 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35905 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35906 this.errorEl.hide();
35908 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35909 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35910 this.footerEl.hide();
35912 this.setThumbBoxSize();
35918 this.fireEvent('initial', this);
35925 window.addEventListener("resize", function() { _this.resize(); } );
35927 this.bodyEl.on('click', this.beforeSelectFile, this);
35930 this.bodyEl.on('touchstart', this.onTouchStart, this);
35931 this.bodyEl.on('touchmove', this.onTouchMove, this);
35932 this.bodyEl.on('touchend', this.onTouchEnd, this);
35936 this.bodyEl.on('mousedown', this.onMouseDown, this);
35937 this.bodyEl.on('mousemove', this.onMouseMove, this);
35938 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35939 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35940 Roo.get(document).on('mouseup', this.onMouseUp, this);
35943 this.selectorEl.on('change', this.onFileSelected, this);
35949 this.baseScale = 1;
35951 this.baseRotate = 1;
35952 this.dragable = false;
35953 this.pinching = false;
35956 this.cropData = false;
35957 this.notifyEl.dom.innerHTML = this.emptyText;
35959 this.selectorEl.dom.value = '';
35963 resize : function()
35965 if(this.fireEvent('resize', this) != false){
35966 this.setThumbBoxPosition();
35967 this.setCanvasPosition();
35971 onFooterButtonClick : function(e, el, o, type)
35974 case 'rotate-left' :
35975 this.onRotateLeft(e);
35977 case 'rotate-right' :
35978 this.onRotateRight(e);
35981 this.beforeSelectFile(e);
35996 this.fireEvent('footerbuttonclick', this, type);
35999 beforeSelectFile : function(e)
36001 e.preventDefault();
36003 if(this.fireEvent('beforeselectfile', this) != false){
36004 this.selectorEl.dom.click();
36008 onFileSelected : function(e)
36010 e.preventDefault();
36012 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36016 var file = this.selectorEl.dom.files[0];
36018 if(this.fireEvent('inspect', this, file) != false){
36019 this.prepare(file);
36024 trash : function(e)
36026 this.fireEvent('trash', this);
36029 download : function(e)
36031 this.fireEvent('download', this);
36034 loadCanvas : function(src)
36036 if(this.fireEvent('beforeloadcanvas', this, src) != false){
36040 this.imageEl = document.createElement('img');
36044 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
36046 this.imageEl.src = src;
36050 onLoadCanvas : function()
36052 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
36053 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
36055 this.bodyEl.un('click', this.beforeSelectFile, this);
36057 this.notifyEl.hide();
36058 this.thumbEl.show();
36059 this.footerEl.show();
36061 this.baseRotateLevel();
36063 if(this.isDocument){
36064 this.setThumbBoxSize();
36067 this.setThumbBoxPosition();
36069 this.baseScaleLevel();
36075 this.canvasLoaded = true;
36078 this.maskEl.unmask();
36083 setCanvasPosition : function()
36085 if(!this.canvasEl){
36089 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
36090 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
36092 this.previewEl.setLeft(pw);
36093 this.previewEl.setTop(ph);
36097 onMouseDown : function(e)
36101 this.dragable = true;
36102 this.pinching = false;
36104 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
36105 this.dragable = false;
36109 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36110 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36114 onMouseMove : function(e)
36118 if(!this.canvasLoaded){
36122 if (!this.dragable){
36126 var minX = Math.ceil(this.thumbEl.getLeft(true));
36127 var minY = Math.ceil(this.thumbEl.getTop(true));
36129 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
36130 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
36132 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36133 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36135 x = x - this.mouseX;
36136 y = y - this.mouseY;
36138 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
36139 var bgY = Math.ceil(y + this.previewEl.getTop(true));
36141 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
36142 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
36144 this.previewEl.setLeft(bgX);
36145 this.previewEl.setTop(bgY);
36147 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
36148 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
36151 onMouseUp : function(e)
36155 this.dragable = false;
36158 onMouseWheel : function(e)
36162 this.startScale = this.scale;
36164 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
36166 if(!this.zoomable()){
36167 this.scale = this.startScale;
36176 zoomable : function()
36178 var minScale = this.thumbEl.getWidth() / this.minWidth;
36180 if(this.minWidth < this.minHeight){
36181 minScale = this.thumbEl.getHeight() / this.minHeight;
36184 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
36185 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
36189 (this.rotate == 0 || this.rotate == 180) &&
36191 width > this.imageEl.OriginWidth ||
36192 height > this.imageEl.OriginHeight ||
36193 (width < this.minWidth && height < this.minHeight)
36201 (this.rotate == 90 || this.rotate == 270) &&
36203 width > this.imageEl.OriginWidth ||
36204 height > this.imageEl.OriginHeight ||
36205 (width < this.minHeight && height < this.minWidth)
36212 !this.isDocument &&
36213 (this.rotate == 0 || this.rotate == 180) &&
36215 width < this.minWidth ||
36216 width > this.imageEl.OriginWidth ||
36217 height < this.minHeight ||
36218 height > this.imageEl.OriginHeight
36225 !this.isDocument &&
36226 (this.rotate == 90 || this.rotate == 270) &&
36228 width < this.minHeight ||
36229 width > this.imageEl.OriginWidth ||
36230 height < this.minWidth ||
36231 height > this.imageEl.OriginHeight
36241 onRotateLeft : function(e)
36243 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36245 var minScale = this.thumbEl.getWidth() / this.minWidth;
36247 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36248 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36250 this.startScale = this.scale;
36252 while (this.getScaleLevel() < minScale){
36254 this.scale = this.scale + 1;
36256 if(!this.zoomable()){
36261 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36262 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36267 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36274 this.scale = this.startScale;
36276 this.onRotateFail();
36281 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
36283 if(this.isDocument){
36284 this.setThumbBoxSize();
36285 this.setThumbBoxPosition();
36286 this.setCanvasPosition();
36291 this.fireEvent('rotate', this, 'left');
36295 onRotateRight : function(e)
36297 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36299 var minScale = this.thumbEl.getWidth() / this.minWidth;
36301 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36302 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36304 this.startScale = this.scale;
36306 while (this.getScaleLevel() < minScale){
36308 this.scale = this.scale + 1;
36310 if(!this.zoomable()){
36315 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36316 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36321 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36328 this.scale = this.startScale;
36330 this.onRotateFail();
36335 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36337 if(this.isDocument){
36338 this.setThumbBoxSize();
36339 this.setThumbBoxPosition();
36340 this.setCanvasPosition();
36345 this.fireEvent('rotate', this, 'right');
36348 onRotateFail : function()
36350 this.errorEl.show(true);
36354 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36359 this.previewEl.dom.innerHTML = '';
36361 var canvasEl = document.createElement("canvas");
36363 var contextEl = canvasEl.getContext("2d");
36365 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36366 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36367 var center = this.imageEl.OriginWidth / 2;
36369 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36370 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36371 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36372 center = this.imageEl.OriginHeight / 2;
36375 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36377 contextEl.translate(center, center);
36378 contextEl.rotate(this.rotate * Math.PI / 180);
36380 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36382 this.canvasEl = document.createElement("canvas");
36384 this.contextEl = this.canvasEl.getContext("2d");
36386 switch (this.rotate) {
36389 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36390 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36392 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36397 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36398 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36400 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36401 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);
36405 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36410 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36411 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36413 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36414 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);
36418 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);
36423 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36424 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36426 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36427 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36431 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);
36438 this.previewEl.appendChild(this.canvasEl);
36440 this.setCanvasPosition();
36445 if(!this.canvasLoaded){
36449 var imageCanvas = document.createElement("canvas");
36451 var imageContext = imageCanvas.getContext("2d");
36453 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36454 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36456 var center = imageCanvas.width / 2;
36458 imageContext.translate(center, center);
36460 imageContext.rotate(this.rotate * Math.PI / 180);
36462 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36464 var canvas = document.createElement("canvas");
36466 var context = canvas.getContext("2d");
36468 canvas.width = this.minWidth;
36469 canvas.height = this.minHeight;
36471 switch (this.rotate) {
36474 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36475 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36477 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36478 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36480 var targetWidth = this.minWidth - 2 * x;
36481 var targetHeight = this.minHeight - 2 * y;
36485 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36486 scale = targetWidth / width;
36489 if(x > 0 && y == 0){
36490 scale = targetHeight / height;
36493 if(x > 0 && y > 0){
36494 scale = targetWidth / width;
36496 if(width < height){
36497 scale = targetHeight / height;
36501 context.scale(scale, scale);
36503 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36504 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36506 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36507 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36509 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36514 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36515 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36517 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36518 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36520 var targetWidth = this.minWidth - 2 * x;
36521 var targetHeight = this.minHeight - 2 * y;
36525 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36526 scale = targetWidth / width;
36529 if(x > 0 && y == 0){
36530 scale = targetHeight / height;
36533 if(x > 0 && y > 0){
36534 scale = targetWidth / width;
36536 if(width < height){
36537 scale = targetHeight / height;
36541 context.scale(scale, scale);
36543 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36544 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36546 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36547 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36549 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36551 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36556 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36557 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36559 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36560 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36562 var targetWidth = this.minWidth - 2 * x;
36563 var targetHeight = this.minHeight - 2 * y;
36567 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36568 scale = targetWidth / width;
36571 if(x > 0 && y == 0){
36572 scale = targetHeight / height;
36575 if(x > 0 && y > 0){
36576 scale = targetWidth / width;
36578 if(width < height){
36579 scale = targetHeight / height;
36583 context.scale(scale, scale);
36585 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36586 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36588 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36589 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36591 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36592 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36594 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36599 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36600 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36602 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36603 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36605 var targetWidth = this.minWidth - 2 * x;
36606 var targetHeight = this.minHeight - 2 * y;
36610 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36611 scale = targetWidth / width;
36614 if(x > 0 && y == 0){
36615 scale = targetHeight / height;
36618 if(x > 0 && y > 0){
36619 scale = targetWidth / width;
36621 if(width < height){
36622 scale = targetHeight / height;
36626 context.scale(scale, scale);
36628 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36629 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36631 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36632 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36634 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36636 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36643 this.cropData = canvas.toDataURL(this.cropType);
36645 if(this.fireEvent('crop', this, this.cropData) !== false){
36646 this.process(this.file, this.cropData);
36653 setThumbBoxSize : function()
36657 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36658 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36659 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36661 this.minWidth = width;
36662 this.minHeight = height;
36664 if(this.rotate == 90 || this.rotate == 270){
36665 this.minWidth = height;
36666 this.minHeight = width;
36671 width = Math.ceil(this.minWidth * height / this.minHeight);
36673 if(this.minWidth > this.minHeight){
36675 height = Math.ceil(this.minHeight * width / this.minWidth);
36678 this.thumbEl.setStyle({
36679 width : width + 'px',
36680 height : height + 'px'
36687 setThumbBoxPosition : function()
36689 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36690 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36692 this.thumbEl.setLeft(x);
36693 this.thumbEl.setTop(y);
36697 baseRotateLevel : function()
36699 this.baseRotate = 1;
36702 typeof(this.exif) != 'undefined' &&
36703 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36704 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36706 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36709 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36713 baseScaleLevel : function()
36717 if(this.isDocument){
36719 if(this.baseRotate == 6 || this.baseRotate == 8){
36721 height = this.thumbEl.getHeight();
36722 this.baseScale = height / this.imageEl.OriginWidth;
36724 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36725 width = this.thumbEl.getWidth();
36726 this.baseScale = width / this.imageEl.OriginHeight;
36732 height = this.thumbEl.getHeight();
36733 this.baseScale = height / this.imageEl.OriginHeight;
36735 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36736 width = this.thumbEl.getWidth();
36737 this.baseScale = width / this.imageEl.OriginWidth;
36743 if(this.baseRotate == 6 || this.baseRotate == 8){
36745 width = this.thumbEl.getHeight();
36746 this.baseScale = width / this.imageEl.OriginHeight;
36748 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36749 height = this.thumbEl.getWidth();
36750 this.baseScale = height / this.imageEl.OriginHeight;
36753 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36754 height = this.thumbEl.getWidth();
36755 this.baseScale = height / this.imageEl.OriginHeight;
36757 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36758 width = this.thumbEl.getHeight();
36759 this.baseScale = width / this.imageEl.OriginWidth;
36766 width = this.thumbEl.getWidth();
36767 this.baseScale = width / this.imageEl.OriginWidth;
36769 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36770 height = this.thumbEl.getHeight();
36771 this.baseScale = height / this.imageEl.OriginHeight;
36774 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36776 height = this.thumbEl.getHeight();
36777 this.baseScale = height / this.imageEl.OriginHeight;
36779 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36780 width = this.thumbEl.getWidth();
36781 this.baseScale = width / this.imageEl.OriginWidth;
36789 getScaleLevel : function()
36791 return this.baseScale * Math.pow(1.1, this.scale);
36794 onTouchStart : function(e)
36796 if(!this.canvasLoaded){
36797 this.beforeSelectFile(e);
36801 var touches = e.browserEvent.touches;
36807 if(touches.length == 1){
36808 this.onMouseDown(e);
36812 if(touches.length != 2){
36818 for(var i = 0, finger; finger = touches[i]; i++){
36819 coords.push(finger.pageX, finger.pageY);
36822 var x = Math.pow(coords[0] - coords[2], 2);
36823 var y = Math.pow(coords[1] - coords[3], 2);
36825 this.startDistance = Math.sqrt(x + y);
36827 this.startScale = this.scale;
36829 this.pinching = true;
36830 this.dragable = false;
36834 onTouchMove : function(e)
36836 if(!this.pinching && !this.dragable){
36840 var touches = e.browserEvent.touches;
36847 this.onMouseMove(e);
36853 for(var i = 0, finger; finger = touches[i]; i++){
36854 coords.push(finger.pageX, finger.pageY);
36857 var x = Math.pow(coords[0] - coords[2], 2);
36858 var y = Math.pow(coords[1] - coords[3], 2);
36860 this.endDistance = Math.sqrt(x + y);
36862 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36864 if(!this.zoomable()){
36865 this.scale = this.startScale;
36873 onTouchEnd : function(e)
36875 this.pinching = false;
36876 this.dragable = false;
36880 process : function(file, crop)
36883 this.maskEl.mask(this.loadingText);
36886 this.xhr = new XMLHttpRequest();
36888 file.xhr = this.xhr;
36890 this.xhr.open(this.method, this.url, true);
36893 "Accept": "application/json",
36894 "Cache-Control": "no-cache",
36895 "X-Requested-With": "XMLHttpRequest"
36898 for (var headerName in headers) {
36899 var headerValue = headers[headerName];
36901 this.xhr.setRequestHeader(headerName, headerValue);
36907 this.xhr.onload = function()
36909 _this.xhrOnLoad(_this.xhr);
36912 this.xhr.onerror = function()
36914 _this.xhrOnError(_this.xhr);
36917 var formData = new FormData();
36919 formData.append('returnHTML', 'NO');
36922 formData.append('crop', crop);
36925 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36926 formData.append(this.paramName, file, file.name);
36929 if(typeof(file.filename) != 'undefined'){
36930 formData.append('filename', file.filename);
36933 if(typeof(file.mimetype) != 'undefined'){
36934 formData.append('mimetype', file.mimetype);
36937 if(this.fireEvent('arrange', this, formData) != false){
36938 this.xhr.send(formData);
36942 xhrOnLoad : function(xhr)
36945 this.maskEl.unmask();
36948 if (xhr.readyState !== 4) {
36949 this.fireEvent('exception', this, xhr);
36953 var response = Roo.decode(xhr.responseText);
36955 if(!response.success){
36956 this.fireEvent('exception', this, xhr);
36960 var response = Roo.decode(xhr.responseText);
36962 this.fireEvent('upload', this, response);
36966 xhrOnError : function()
36969 this.maskEl.unmask();
36972 Roo.log('xhr on error');
36974 var response = Roo.decode(xhr.responseText);
36980 prepare : function(file)
36983 this.maskEl.mask(this.loadingText);
36989 if(typeof(file) === 'string'){
36990 this.loadCanvas(file);
36994 if(!file || !this.urlAPI){
36999 this.cropType = file.type;
37003 if(this.fireEvent('prepare', this, this.file) != false){
37005 var reader = new FileReader();
37007 reader.onload = function (e) {
37008 if (e.target.error) {
37009 Roo.log(e.target.error);
37013 var buffer = e.target.result,
37014 dataView = new DataView(buffer),
37016 maxOffset = dataView.byteLength - 4,
37020 if (dataView.getUint16(0) === 0xffd8) {
37021 while (offset < maxOffset) {
37022 markerBytes = dataView.getUint16(offset);
37024 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
37025 markerLength = dataView.getUint16(offset + 2) + 2;
37026 if (offset + markerLength > dataView.byteLength) {
37027 Roo.log('Invalid meta data: Invalid segment size.');
37031 if(markerBytes == 0xffe1){
37032 _this.parseExifData(
37039 offset += markerLength;
37049 var url = _this.urlAPI.createObjectURL(_this.file);
37051 _this.loadCanvas(url);
37056 reader.readAsArrayBuffer(this.file);
37062 parseExifData : function(dataView, offset, length)
37064 var tiffOffset = offset + 10,
37068 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37069 // No Exif data, might be XMP data instead
37073 // Check for the ASCII code for "Exif" (0x45786966):
37074 if (dataView.getUint32(offset + 4) !== 0x45786966) {
37075 // No Exif data, might be XMP data instead
37078 if (tiffOffset + 8 > dataView.byteLength) {
37079 Roo.log('Invalid Exif data: Invalid segment size.');
37082 // Check for the two null bytes:
37083 if (dataView.getUint16(offset + 8) !== 0x0000) {
37084 Roo.log('Invalid Exif data: Missing byte alignment offset.');
37087 // Check the byte alignment:
37088 switch (dataView.getUint16(tiffOffset)) {
37090 littleEndian = true;
37093 littleEndian = false;
37096 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
37099 // Check for the TIFF tag marker (0x002A):
37100 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
37101 Roo.log('Invalid Exif data: Missing TIFF marker.');
37104 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
37105 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
37107 this.parseExifTags(
37110 tiffOffset + dirOffset,
37115 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
37120 if (dirOffset + 6 > dataView.byteLength) {
37121 Roo.log('Invalid Exif data: Invalid directory offset.');
37124 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
37125 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
37126 if (dirEndOffset + 4 > dataView.byteLength) {
37127 Roo.log('Invalid Exif data: Invalid directory size.');
37130 for (i = 0; i < tagsNumber; i += 1) {
37134 dirOffset + 2 + 12 * i, // tag offset
37138 // Return the offset to the next directory:
37139 return dataView.getUint32(dirEndOffset, littleEndian);
37142 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
37144 var tag = dataView.getUint16(offset, littleEndian);
37146 this.exif[tag] = this.getExifValue(
37150 dataView.getUint16(offset + 2, littleEndian), // tag type
37151 dataView.getUint32(offset + 4, littleEndian), // tag length
37156 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
37158 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
37167 Roo.log('Invalid Exif data: Invalid tag type.');
37171 tagSize = tagType.size * length;
37172 // Determine if the value is contained in the dataOffset bytes,
37173 // or if the value at the dataOffset is a pointer to the actual data:
37174 dataOffset = tagSize > 4 ?
37175 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
37176 if (dataOffset + tagSize > dataView.byteLength) {
37177 Roo.log('Invalid Exif data: Invalid data offset.');
37180 if (length === 1) {
37181 return tagType.getValue(dataView, dataOffset, littleEndian);
37184 for (i = 0; i < length; i += 1) {
37185 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
37188 if (tagType.ascii) {
37190 // Concatenate the chars:
37191 for (i = 0; i < values.length; i += 1) {
37193 // Ignore the terminating NULL byte(s):
37194 if (c === '\u0000') {
37206 Roo.apply(Roo.bootstrap.UploadCropbox, {
37208 'Orientation': 0x0112
37212 1: 0, //'top-left',
37214 3: 180, //'bottom-right',
37215 // 4: 'bottom-left',
37217 6: 90, //'right-top',
37218 // 7: 'right-bottom',
37219 8: 270 //'left-bottom'
37223 // byte, 8-bit unsigned int:
37225 getValue: function (dataView, dataOffset) {
37226 return dataView.getUint8(dataOffset);
37230 // ascii, 8-bit byte:
37232 getValue: function (dataView, dataOffset) {
37233 return String.fromCharCode(dataView.getUint8(dataOffset));
37238 // short, 16 bit int:
37240 getValue: function (dataView, dataOffset, littleEndian) {
37241 return dataView.getUint16(dataOffset, littleEndian);
37245 // long, 32 bit int:
37247 getValue: function (dataView, dataOffset, littleEndian) {
37248 return dataView.getUint32(dataOffset, littleEndian);
37252 // rational = two long values, first is numerator, second is denominator:
37254 getValue: function (dataView, dataOffset, littleEndian) {
37255 return dataView.getUint32(dataOffset, littleEndian) /
37256 dataView.getUint32(dataOffset + 4, littleEndian);
37260 // slong, 32 bit signed int:
37262 getValue: function (dataView, dataOffset, littleEndian) {
37263 return dataView.getInt32(dataOffset, littleEndian);
37267 // srational, two slongs, first is numerator, second is denominator:
37269 getValue: function (dataView, dataOffset, littleEndian) {
37270 return dataView.getInt32(dataOffset, littleEndian) /
37271 dataView.getInt32(dataOffset + 4, littleEndian);
37281 cls : 'btn-group roo-upload-cropbox-rotate-left',
37282 action : 'rotate-left',
37286 cls : 'btn btn-default',
37287 html : '<i class="fa fa-undo"></i>'
37293 cls : 'btn-group roo-upload-cropbox-picture',
37294 action : 'picture',
37298 cls : 'btn btn-default',
37299 html : '<i class="fa fa-picture-o"></i>'
37305 cls : 'btn-group roo-upload-cropbox-rotate-right',
37306 action : 'rotate-right',
37310 cls : 'btn btn-default',
37311 html : '<i class="fa fa-repeat"></i>'
37319 cls : 'btn-group roo-upload-cropbox-rotate-left',
37320 action : 'rotate-left',
37324 cls : 'btn btn-default',
37325 html : '<i class="fa fa-undo"></i>'
37331 cls : 'btn-group roo-upload-cropbox-download',
37332 action : 'download',
37336 cls : 'btn btn-default',
37337 html : '<i class="fa fa-download"></i>'
37343 cls : 'btn-group roo-upload-cropbox-crop',
37348 cls : 'btn btn-default',
37349 html : '<i class="fa fa-crop"></i>'
37355 cls : 'btn-group roo-upload-cropbox-trash',
37360 cls : 'btn btn-default',
37361 html : '<i class="fa fa-trash"></i>'
37367 cls : 'btn-group roo-upload-cropbox-rotate-right',
37368 action : 'rotate-right',
37372 cls : 'btn btn-default',
37373 html : '<i class="fa fa-repeat"></i>'
37381 cls : 'btn-group roo-upload-cropbox-rotate-left',
37382 action : 'rotate-left',
37386 cls : 'btn btn-default',
37387 html : '<i class="fa fa-undo"></i>'
37393 cls : 'btn-group roo-upload-cropbox-rotate-right',
37394 action : 'rotate-right',
37398 cls : 'btn btn-default',
37399 html : '<i class="fa fa-repeat"></i>'
37412 * @class Roo.bootstrap.DocumentManager
37413 * @extends Roo.bootstrap.Component
37414 * Bootstrap DocumentManager class
37415 * @cfg {String} paramName default 'imageUpload'
37416 * @cfg {String} toolTipName default 'filename'
37417 * @cfg {String} method default POST
37418 * @cfg {String} url action url
37419 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37420 * @cfg {Boolean} multiple multiple upload default true
37421 * @cfg {Number} thumbSize default 300
37422 * @cfg {String} fieldLabel
37423 * @cfg {Number} labelWidth default 4
37424 * @cfg {String} labelAlign (left|top) default left
37425 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37426 * @cfg {Number} labellg set the width of label (1-12)
37427 * @cfg {Number} labelmd set the width of label (1-12)
37428 * @cfg {Number} labelsm set the width of label (1-12)
37429 * @cfg {Number} labelxs set the width of label (1-12)
37432 * Create a new DocumentManager
37433 * @param {Object} config The config object
37436 Roo.bootstrap.DocumentManager = function(config){
37437 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37440 this.delegates = [];
37445 * Fire when initial the DocumentManager
37446 * @param {Roo.bootstrap.DocumentManager} this
37451 * inspect selected file
37452 * @param {Roo.bootstrap.DocumentManager} this
37453 * @param {File} file
37458 * Fire when xhr load exception
37459 * @param {Roo.bootstrap.DocumentManager} this
37460 * @param {XMLHttpRequest} xhr
37462 "exception" : true,
37464 * @event afterupload
37465 * Fire when xhr load exception
37466 * @param {Roo.bootstrap.DocumentManager} this
37467 * @param {XMLHttpRequest} xhr
37469 "afterupload" : true,
37472 * prepare the form data
37473 * @param {Roo.bootstrap.DocumentManager} this
37474 * @param {Object} formData
37479 * Fire when remove the file
37480 * @param {Roo.bootstrap.DocumentManager} this
37481 * @param {Object} file
37486 * Fire after refresh the file
37487 * @param {Roo.bootstrap.DocumentManager} this
37492 * Fire after click the image
37493 * @param {Roo.bootstrap.DocumentManager} this
37494 * @param {Object} file
37499 * Fire when upload a image and editable set to true
37500 * @param {Roo.bootstrap.DocumentManager} this
37501 * @param {Object} file
37505 * @event beforeselectfile
37506 * Fire before select file
37507 * @param {Roo.bootstrap.DocumentManager} this
37509 "beforeselectfile" : true,
37512 * Fire before process file
37513 * @param {Roo.bootstrap.DocumentManager} this
37514 * @param {Object} file
37518 * @event previewrendered
37519 * Fire when preview rendered
37520 * @param {Roo.bootstrap.DocumentManager} this
37521 * @param {Object} file
37523 "previewrendered" : true,
37526 "previewResize" : true
37531 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
37540 paramName : 'imageUpload',
37541 toolTipName : 'filename',
37544 labelAlign : 'left',
37554 getAutoCreate : function()
37556 var managerWidget = {
37558 cls : 'roo-document-manager',
37562 cls : 'roo-document-manager-selector',
37567 cls : 'roo-document-manager-uploader',
37571 cls : 'roo-document-manager-upload-btn',
37572 html : '<i class="fa fa-plus"></i>'
37583 cls : 'column col-md-12',
37588 if(this.fieldLabel.length){
37593 cls : 'column col-md-12',
37594 html : this.fieldLabel
37598 cls : 'column col-md-12',
37603 if(this.labelAlign == 'left'){
37608 html : this.fieldLabel
37617 if(this.labelWidth > 12){
37618 content[0].style = "width: " + this.labelWidth + 'px';
37621 if(this.labelWidth < 13 && this.labelmd == 0){
37622 this.labelmd = this.labelWidth;
37625 if(this.labellg > 0){
37626 content[0].cls += ' col-lg-' + this.labellg;
37627 content[1].cls += ' col-lg-' + (12 - this.labellg);
37630 if(this.labelmd > 0){
37631 content[0].cls += ' col-md-' + this.labelmd;
37632 content[1].cls += ' col-md-' + (12 - this.labelmd);
37635 if(this.labelsm > 0){
37636 content[0].cls += ' col-sm-' + this.labelsm;
37637 content[1].cls += ' col-sm-' + (12 - this.labelsm);
37640 if(this.labelxs > 0){
37641 content[0].cls += ' col-xs-' + this.labelxs;
37642 content[1].cls += ' col-xs-' + (12 - this.labelxs);
37650 cls : 'row clearfix',
37658 initEvents : function()
37660 this.managerEl = this.el.select('.roo-document-manager', true).first();
37661 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37663 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37664 this.selectorEl.hide();
37667 this.selectorEl.attr('multiple', 'multiple');
37670 this.selectorEl.on('change', this.onFileSelected, this);
37672 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37673 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37675 this.uploader.on('click', this.onUploaderClick, this);
37677 this.renderProgressDialog();
37681 window.addEventListener("resize", function() { _this.refresh(); } );
37683 this.fireEvent('initial', this);
37686 renderProgressDialog : function()
37690 this.progressDialog = new Roo.bootstrap.Modal({
37691 cls : 'roo-document-manager-progress-dialog',
37692 allow_close : false,
37703 btnclick : function() {
37704 _this.uploadCancel();
37710 this.progressDialog.render(Roo.get(document.body));
37712 this.progress = new Roo.bootstrap.Progress({
37713 cls : 'roo-document-manager-progress',
37718 this.progress.render(this.progressDialog.getChildContainer());
37720 this.progressBar = new Roo.bootstrap.ProgressBar({
37721 cls : 'roo-document-manager-progress-bar',
37724 aria_valuemax : 12,
37728 this.progressBar.render(this.progress.getChildContainer());
37731 onUploaderClick : function(e)
37733 e.preventDefault();
37735 if(this.fireEvent('beforeselectfile', this) != false){
37736 this.selectorEl.dom.click();
37741 onFileSelected : function(e)
37743 e.preventDefault();
37745 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37749 Roo.each(this.selectorEl.dom.files, function(file){
37750 if(this.fireEvent('inspect', this, file) != false){
37751 this.files.push(file);
37761 this.selectorEl.dom.value = '';
37763 if(!this.files || !this.files.length){
37767 if(this.boxes > 0 && this.files.length > this.boxes){
37768 this.files = this.files.slice(0, this.boxes);
37771 this.uploader.show();
37773 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37774 this.uploader.hide();
37783 Roo.each(this.files, function(file){
37785 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37786 var f = this.renderPreview(file);
37791 if(file.type.indexOf('image') != -1){
37792 this.delegates.push(
37794 _this.process(file);
37795 }).createDelegate(this)
37803 _this.process(file);
37804 }).createDelegate(this)
37809 this.files = files;
37811 this.delegates = this.delegates.concat(docs);
37813 if(!this.delegates.length){
37818 this.progressBar.aria_valuemax = this.delegates.length;
37825 arrange : function()
37827 if(!this.delegates.length){
37828 this.progressDialog.hide();
37833 var delegate = this.delegates.shift();
37835 this.progressDialog.show();
37837 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37839 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37844 refresh : function()
37846 this.uploader.show();
37848 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37849 this.uploader.hide();
37852 Roo.isTouch ? this.closable(false) : this.closable(true);
37854 this.fireEvent('refresh', this);
37857 onRemove : function(e, el, o)
37859 e.preventDefault();
37861 this.fireEvent('remove', this, o);
37865 remove : function(o)
37869 Roo.each(this.files, function(file){
37870 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37879 this.files = files;
37886 Roo.each(this.files, function(file){
37891 file.target.remove();
37900 onClick : function(e, el, o)
37902 e.preventDefault();
37904 this.fireEvent('click', this, o);
37908 closable : function(closable)
37910 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37912 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37924 xhrOnLoad : function(xhr)
37926 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37930 if (xhr.readyState !== 4) {
37932 this.fireEvent('exception', this, xhr);
37936 var response = Roo.decode(xhr.responseText);
37938 if(!response.success){
37940 this.fireEvent('exception', this, xhr);
37944 var file = this.renderPreview(response.data);
37946 this.files.push(file);
37950 this.fireEvent('afterupload', this, xhr);
37954 xhrOnError : function(xhr)
37956 Roo.log('xhr on error');
37958 var response = Roo.decode(xhr.responseText);
37965 process : function(file)
37967 if(this.fireEvent('process', this, file) !== false){
37968 if(this.editable && file.type.indexOf('image') != -1){
37969 this.fireEvent('edit', this, file);
37973 this.uploadStart(file, false);
37980 uploadStart : function(file, crop)
37982 this.xhr = new XMLHttpRequest();
37984 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37989 file.xhr = this.xhr;
37991 this.managerEl.createChild({
37993 cls : 'roo-document-manager-loading',
37997 tooltip : file.name,
37998 cls : 'roo-document-manager-thumb',
37999 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38005 this.xhr.open(this.method, this.url, true);
38008 "Accept": "application/json",
38009 "Cache-Control": "no-cache",
38010 "X-Requested-With": "XMLHttpRequest"
38013 for (var headerName in headers) {
38014 var headerValue = headers[headerName];
38016 this.xhr.setRequestHeader(headerName, headerValue);
38022 this.xhr.onload = function()
38024 _this.xhrOnLoad(_this.xhr);
38027 this.xhr.onerror = function()
38029 _this.xhrOnError(_this.xhr);
38032 var formData = new FormData();
38034 formData.append('returnHTML', 'NO');
38037 formData.append('crop', crop);
38040 formData.append(this.paramName, file, file.name);
38047 if(this.fireEvent('prepare', this, formData, options) != false){
38049 if(options.manually){
38053 this.xhr.send(formData);
38057 this.uploadCancel();
38060 uploadCancel : function()
38066 this.delegates = [];
38068 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
38075 renderPreview : function(file)
38077 if(typeof(file.target) != 'undefined' && file.target){
38081 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
38083 var previewEl = this.managerEl.createChild({
38085 cls : 'roo-document-manager-preview',
38089 tooltip : file[this.toolTipName],
38090 cls : 'roo-document-manager-thumb',
38091 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
38096 html : '<i class="fa fa-times-circle"></i>'
38101 var close = previewEl.select('button.close', true).first();
38103 close.on('click', this.onRemove, this, file);
38105 file.target = previewEl;
38107 var image = previewEl.select('img', true).first();
38111 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
38113 image.on('click', this.onClick, this, file);
38115 this.fireEvent('previewrendered', this, file);
38121 onPreviewLoad : function(file, image)
38123 if(typeof(file.target) == 'undefined' || !file.target){
38127 var width = image.dom.naturalWidth || image.dom.width;
38128 var height = image.dom.naturalHeight || image.dom.height;
38130 if(!this.previewResize) {
38134 if(width > height){
38135 file.target.addClass('wide');
38139 file.target.addClass('tall');
38144 uploadFromSource : function(file, crop)
38146 this.xhr = new XMLHttpRequest();
38148 this.managerEl.createChild({
38150 cls : 'roo-document-manager-loading',
38154 tooltip : file.name,
38155 cls : 'roo-document-manager-thumb',
38156 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
38162 this.xhr.open(this.method, this.url, true);
38165 "Accept": "application/json",
38166 "Cache-Control": "no-cache",
38167 "X-Requested-With": "XMLHttpRequest"
38170 for (var headerName in headers) {
38171 var headerValue = headers[headerName];
38173 this.xhr.setRequestHeader(headerName, headerValue);
38179 this.xhr.onload = function()
38181 _this.xhrOnLoad(_this.xhr);
38184 this.xhr.onerror = function()
38186 _this.xhrOnError(_this.xhr);
38189 var formData = new FormData();
38191 formData.append('returnHTML', 'NO');
38193 formData.append('crop', crop);
38195 if(typeof(file.filename) != 'undefined'){
38196 formData.append('filename', file.filename);
38199 if(typeof(file.mimetype) != 'undefined'){
38200 formData.append('mimetype', file.mimetype);
38205 if(this.fireEvent('prepare', this, formData) != false){
38206 this.xhr.send(formData);
38216 * @class Roo.bootstrap.DocumentViewer
38217 * @extends Roo.bootstrap.Component
38218 * Bootstrap DocumentViewer class
38219 * @cfg {Boolean} showDownload (true|false) show download button (default true)
38220 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
38223 * Create a new DocumentViewer
38224 * @param {Object} config The config object
38227 Roo.bootstrap.DocumentViewer = function(config){
38228 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
38233 * Fire after initEvent
38234 * @param {Roo.bootstrap.DocumentViewer} this
38240 * @param {Roo.bootstrap.DocumentViewer} this
38245 * Fire after download button
38246 * @param {Roo.bootstrap.DocumentViewer} this
38251 * Fire after trash button
38252 * @param {Roo.bootstrap.DocumentViewer} this
38259 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
38261 showDownload : true,
38265 getAutoCreate : function()
38269 cls : 'roo-document-viewer',
38273 cls : 'roo-document-viewer-body',
38277 cls : 'roo-document-viewer-thumb',
38281 cls : 'roo-document-viewer-image'
38289 cls : 'roo-document-viewer-footer',
38292 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38296 cls : 'btn-group roo-document-viewer-download',
38300 cls : 'btn btn-default',
38301 html : '<i class="fa fa-download"></i>'
38307 cls : 'btn-group roo-document-viewer-trash',
38311 cls : 'btn btn-default',
38312 html : '<i class="fa fa-trash"></i>'
38325 initEvents : function()
38327 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38328 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38330 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38331 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38333 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38334 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38336 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38337 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38339 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38340 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38342 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38343 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38345 this.bodyEl.on('click', this.onClick, this);
38346 this.downloadBtn.on('click', this.onDownload, this);
38347 this.trashBtn.on('click', this.onTrash, this);
38349 this.downloadBtn.hide();
38350 this.trashBtn.hide();
38352 if(this.showDownload){
38353 this.downloadBtn.show();
38356 if(this.showTrash){
38357 this.trashBtn.show();
38360 if(!this.showDownload && !this.showTrash) {
38361 this.footerEl.hide();
38366 initial : function()
38368 this.fireEvent('initial', this);
38372 onClick : function(e)
38374 e.preventDefault();
38376 this.fireEvent('click', this);
38379 onDownload : function(e)
38381 e.preventDefault();
38383 this.fireEvent('download', this);
38386 onTrash : function(e)
38388 e.preventDefault();
38390 this.fireEvent('trash', this);
38402 * @class Roo.bootstrap.form.FieldLabel
38403 * @extends Roo.bootstrap.Component
38404 * Bootstrap FieldLabel class
38405 * @cfg {String} html contents of the element
38406 * @cfg {String} tag tag of the element default label
38407 * @cfg {String} cls class of the element
38408 * @cfg {String} target label target
38409 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38410 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38411 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38412 * @cfg {String} iconTooltip default "This field is required"
38413 * @cfg {String} indicatorpos (left|right) default left
38416 * Create a new FieldLabel
38417 * @param {Object} config The config object
38420 Roo.bootstrap.form.FieldLabel = function(config){
38421 Roo.bootstrap.Element.superclass.constructor.call(this, config);
38426 * Fires after the field has been marked as invalid.
38427 * @param {Roo.form.FieldLabel} this
38428 * @param {String} msg The validation message
38433 * Fires after the field has been validated with no errors.
38434 * @param {Roo.form.FieldLabel} this
38440 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
38447 invalidClass : 'has-warning',
38448 validClass : 'has-success',
38449 iconTooltip : 'This field is required',
38450 indicatorpos : 'left',
38452 getAutoCreate : function(){
38455 if (!this.allowBlank) {
38461 cls : 'roo-bootstrap-field-label ' + this.cls,
38466 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38467 tooltip : this.iconTooltip
38476 if(this.indicatorpos == 'right'){
38479 cls : 'roo-bootstrap-field-label ' + this.cls,
38488 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38489 tooltip : this.iconTooltip
38498 initEvents: function()
38500 Roo.bootstrap.Element.superclass.initEvents.call(this);
38502 this.indicator = this.indicatorEl();
38504 if(this.indicator){
38505 this.indicator.removeClass('visible');
38506 this.indicator.addClass('invisible');
38509 Roo.bootstrap.form.FieldLabel.register(this);
38512 indicatorEl : function()
38514 var indicator = this.el.select('i.roo-required-indicator',true).first();
38525 * Mark this field as valid
38527 markValid : function()
38529 if(this.indicator){
38530 this.indicator.removeClass('visible');
38531 this.indicator.addClass('invisible');
38533 if (Roo.bootstrap.version == 3) {
38534 this.el.removeClass(this.invalidClass);
38535 this.el.addClass(this.validClass);
38537 this.el.removeClass('is-invalid');
38538 this.el.addClass('is-valid');
38542 this.fireEvent('valid', this);
38546 * Mark this field as invalid
38547 * @param {String} msg The validation message
38549 markInvalid : function(msg)
38551 if(this.indicator){
38552 this.indicator.removeClass('invisible');
38553 this.indicator.addClass('visible');
38555 if (Roo.bootstrap.version == 3) {
38556 this.el.removeClass(this.validClass);
38557 this.el.addClass(this.invalidClass);
38559 this.el.removeClass('is-valid');
38560 this.el.addClass('is-invalid');
38564 this.fireEvent('invalid', this, msg);
38570 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38575 * register a FieldLabel Group
38576 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38578 register : function(label)
38580 if(this.groups.hasOwnProperty(label.target)){
38584 this.groups[label.target] = label;
38588 * fetch a FieldLabel Group based on the target
38589 * @param {string} target
38590 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38592 get: function(target) {
38593 if (typeof(this.groups[target]) == 'undefined') {
38597 return this.groups[target] ;
38606 * page DateSplitField.
38612 * @class Roo.bootstrap.form.DateSplitField
38613 * @extends Roo.bootstrap.Component
38614 * Bootstrap DateSplitField class
38615 * @cfg {string} fieldLabel - the label associated
38616 * @cfg {Number} labelWidth set the width of label (0-12)
38617 * @cfg {String} labelAlign (top|left)
38618 * @cfg {Boolean} dayAllowBlank (true|false) default false
38619 * @cfg {Boolean} monthAllowBlank (true|false) default false
38620 * @cfg {Boolean} yearAllowBlank (true|false) default false
38621 * @cfg {string} dayPlaceholder
38622 * @cfg {string} monthPlaceholder
38623 * @cfg {string} yearPlaceholder
38624 * @cfg {string} dayFormat default 'd'
38625 * @cfg {string} monthFormat default 'm'
38626 * @cfg {string} yearFormat default 'Y'
38627 * @cfg {Number} labellg set the width of label (1-12)
38628 * @cfg {Number} labelmd set the width of label (1-12)
38629 * @cfg {Number} labelsm set the width of label (1-12)
38630 * @cfg {Number} labelxs set the width of label (1-12)
38634 * Create a new DateSplitField
38635 * @param {Object} config The config object
38638 Roo.bootstrap.form.DateSplitField = function(config){
38639 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38645 * getting the data of years
38646 * @param {Roo.bootstrap.form.DateSplitField} this
38647 * @param {Object} years
38652 * getting the data of days
38653 * @param {Roo.bootstrap.form.DateSplitField} this
38654 * @param {Object} days
38659 * Fires after the field has been marked as invalid.
38660 * @param {Roo.form.Field} this
38661 * @param {String} msg The validation message
38666 * Fires after the field has been validated with no errors.
38667 * @param {Roo.form.Field} this
38673 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
38676 labelAlign : 'top',
38678 dayAllowBlank : false,
38679 monthAllowBlank : false,
38680 yearAllowBlank : false,
38681 dayPlaceholder : '',
38682 monthPlaceholder : '',
38683 yearPlaceholder : '',
38687 isFormField : true,
38693 getAutoCreate : function()
38697 cls : 'row roo-date-split-field-group',
38702 cls : 'form-hidden-field roo-date-split-field-group-value',
38708 var labelCls = 'col-md-12';
38709 var contentCls = 'col-md-4';
38711 if(this.fieldLabel){
38715 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38719 html : this.fieldLabel
38724 if(this.labelAlign == 'left'){
38726 if(this.labelWidth > 12){
38727 label.style = "width: " + this.labelWidth + 'px';
38730 if(this.labelWidth < 13 && this.labelmd == 0){
38731 this.labelmd = this.labelWidth;
38734 if(this.labellg > 0){
38735 labelCls = ' col-lg-' + this.labellg;
38736 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38739 if(this.labelmd > 0){
38740 labelCls = ' col-md-' + this.labelmd;
38741 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38744 if(this.labelsm > 0){
38745 labelCls = ' col-sm-' + this.labelsm;
38746 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38749 if(this.labelxs > 0){
38750 labelCls = ' col-xs-' + this.labelxs;
38751 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38755 label.cls += ' ' + labelCls;
38757 cfg.cn.push(label);
38760 Roo.each(['day', 'month', 'year'], function(t){
38763 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38770 inputEl: function ()
38772 return this.el.select('.roo-date-split-field-group-value', true).first();
38775 onRender : function(ct, position)
38779 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38781 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38783 this.dayField = new Roo.bootstrap.form.ComboBox({
38784 allowBlank : this.dayAllowBlank,
38785 alwaysQuery : true,
38786 displayField : 'value',
38789 forceSelection : true,
38791 placeholder : this.dayPlaceholder,
38792 selectOnFocus : true,
38793 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38794 triggerAction : 'all',
38796 valueField : 'value',
38797 store : new Roo.data.SimpleStore({
38798 data : (function() {
38800 _this.fireEvent('days', _this, days);
38803 fields : [ 'value' ]
38806 select : function (_self, record, index)
38808 _this.setValue(_this.getValue());
38813 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38815 this.monthField = new Roo.bootstrap.form.MonthField({
38816 after : '<i class=\"fa fa-calendar\"></i>',
38817 allowBlank : this.monthAllowBlank,
38818 placeholder : this.monthPlaceholder,
38821 render : function (_self)
38823 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38824 e.preventDefault();
38828 select : function (_self, oldvalue, newvalue)
38830 _this.setValue(_this.getValue());
38835 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38837 this.yearField = new Roo.bootstrap.form.ComboBox({
38838 allowBlank : this.yearAllowBlank,
38839 alwaysQuery : true,
38840 displayField : 'value',
38843 forceSelection : true,
38845 placeholder : this.yearPlaceholder,
38846 selectOnFocus : true,
38847 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38848 triggerAction : 'all',
38850 valueField : 'value',
38851 store : new Roo.data.SimpleStore({
38852 data : (function() {
38854 _this.fireEvent('years', _this, years);
38857 fields : [ 'value' ]
38860 select : function (_self, record, index)
38862 _this.setValue(_this.getValue());
38867 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38870 setValue : function(v, format)
38872 this.inputEl.dom.value = v;
38874 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38876 var d = Date.parseDate(v, f);
38883 this.setDay(d.format(this.dayFormat));
38884 this.setMonth(d.format(this.monthFormat));
38885 this.setYear(d.format(this.yearFormat));
38892 setDay : function(v)
38894 this.dayField.setValue(v);
38895 this.inputEl.dom.value = this.getValue();
38900 setMonth : function(v)
38902 this.monthField.setValue(v, true);
38903 this.inputEl.dom.value = this.getValue();
38908 setYear : function(v)
38910 this.yearField.setValue(v);
38911 this.inputEl.dom.value = this.getValue();
38916 getDay : function()
38918 return this.dayField.getValue();
38921 getMonth : function()
38923 return this.monthField.getValue();
38926 getYear : function()
38928 return this.yearField.getValue();
38931 getValue : function()
38933 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38935 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38945 this.inputEl.dom.value = '';
38950 validate : function()
38952 var d = this.dayField.validate();
38953 var m = this.monthField.validate();
38954 var y = this.yearField.validate();
38959 (!this.dayAllowBlank && !d) ||
38960 (!this.monthAllowBlank && !m) ||
38961 (!this.yearAllowBlank && !y)
38966 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38975 this.markInvalid();
38980 markValid : function()
38983 var label = this.el.select('label', true).first();
38984 var icon = this.el.select('i.fa-star', true).first();
38990 this.fireEvent('valid', this);
38994 * Mark this field as invalid
38995 * @param {String} msg The validation message
38997 markInvalid : function(msg)
39000 var label = this.el.select('label', true).first();
39001 var icon = this.el.select('i.fa-star', true).first();
39003 if(label && !icon){
39004 this.el.select('.roo-date-split-field-label', true).createChild({
39006 cls : 'text-danger fa fa-lg fa-star',
39007 tooltip : 'This field is required',
39008 style : 'margin-right:5px;'
39012 this.fireEvent('invalid', this, msg);
39015 clearInvalid : function()
39017 var label = this.el.select('label', true).first();
39018 var icon = this.el.select('i.fa-star', true).first();
39024 this.fireEvent('valid', this);
39027 getName: function()
39037 * @class Roo.bootstrap.LayoutMasonry
39038 * @extends Roo.bootstrap.Component
39039 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
39040 * Bootstrap Layout Masonry class
39043 * http://masonry.desandro.com
39045 * The idea is to render all the bricks based on vertical width...
39047 * The original code extends 'outlayer' - we might need to use that....
39050 * Create a new Element
39051 * @param {Object} config The config object
39054 Roo.bootstrap.LayoutMasonry = function(config){
39056 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
39060 Roo.bootstrap.LayoutMasonry.register(this);
39066 * Fire after layout the items
39067 * @param {Roo.bootstrap.LayoutMasonry} this
39068 * @param {Roo.EventObject} e
39075 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
39078 * @cfg {Boolean} isLayoutInstant = no animation?
39080 isLayoutInstant : false, // needed?
39083 * @cfg {Number} boxWidth width of the columns
39088 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
39093 * @cfg {Number} padWidth padding below box..
39098 * @cfg {Number} gutter gutter width..
39103 * @cfg {Number} maxCols maximum number of columns
39109 * @cfg {Boolean} isAutoInitial defalut true
39111 isAutoInitial : true,
39116 * @cfg {Boolean} isHorizontal defalut false
39118 isHorizontal : false,
39120 currentSize : null,
39126 bricks: null, //CompositeElement
39130 _isLayoutInited : false,
39132 // isAlternative : false, // only use for vertical layout...
39135 * @cfg {Number} alternativePadWidth padding below box..
39137 alternativePadWidth : 50,
39139 selectedBrick : [],
39141 getAutoCreate : function(){
39143 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
39147 cls: 'blog-masonary-wrapper ' + this.cls,
39149 cls : 'mas-boxes masonary'
39156 getChildContainer: function( )
39158 if (this.boxesEl) {
39159 return this.boxesEl;
39162 this.boxesEl = this.el.select('.mas-boxes').first();
39164 return this.boxesEl;
39168 initEvents : function()
39172 if(this.isAutoInitial){
39173 Roo.log('hook children rendered');
39174 this.on('childrenrendered', function() {
39175 Roo.log('children rendered');
39181 initial : function()
39183 this.selectedBrick = [];
39185 this.currentSize = this.el.getBox(true);
39187 Roo.EventManager.onWindowResize(this.resize, this);
39189 if(!this.isAutoInitial){
39197 //this.layout.defer(500,this);
39201 resize : function()
39203 var cs = this.el.getBox(true);
39206 this.currentSize.width == cs.width &&
39207 this.currentSize.x == cs.x &&
39208 this.currentSize.height == cs.height &&
39209 this.currentSize.y == cs.y
39211 Roo.log("no change in with or X or Y");
39215 this.currentSize = cs;
39221 layout : function()
39223 this._resetLayout();
39225 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39227 this.layoutItems( isInstant );
39229 this._isLayoutInited = true;
39231 this.fireEvent('layout', this);
39235 _resetLayout : function()
39237 if(this.isHorizontal){
39238 this.horizontalMeasureColumns();
39242 this.verticalMeasureColumns();
39246 verticalMeasureColumns : function()
39248 this.getContainerWidth();
39250 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39251 // this.colWidth = Math.floor(this.containerWidth * 0.8);
39255 var boxWidth = this.boxWidth + this.padWidth;
39257 if(this.containerWidth < this.boxWidth){
39258 boxWidth = this.containerWidth
39261 var containerWidth = this.containerWidth;
39263 var cols = Math.floor(containerWidth / boxWidth);
39265 this.cols = Math.max( cols, 1 );
39267 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39269 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
39271 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
39273 this.colWidth = boxWidth + avail - this.padWidth;
39275 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
39276 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
39279 horizontalMeasureColumns : function()
39281 this.getContainerWidth();
39283 var boxWidth = this.boxWidth;
39285 if(this.containerWidth < boxWidth){
39286 boxWidth = this.containerWidth;
39289 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39291 this.el.setHeight(boxWidth);
39295 getContainerWidth : function()
39297 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
39300 layoutItems : function( isInstant )
39302 Roo.log(this.bricks);
39304 var items = Roo.apply([], this.bricks);
39306 if(this.isHorizontal){
39307 this._horizontalLayoutItems( items , isInstant );
39311 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39312 // this._verticalAlternativeLayoutItems( items , isInstant );
39316 this._verticalLayoutItems( items , isInstant );
39320 _verticalLayoutItems : function ( items , isInstant)
39322 if ( !items || !items.length ) {
39327 ['xs', 'xs', 'xs', 'tall'],
39328 ['xs', 'xs', 'tall'],
39329 ['xs', 'xs', 'sm'],
39330 ['xs', 'xs', 'xs'],
39336 ['sm', 'xs', 'xs'],
39340 ['tall', 'xs', 'xs', 'xs'],
39341 ['tall', 'xs', 'xs'],
39353 Roo.each(items, function(item, k){
39355 switch (item.size) {
39356 // these layouts take up a full box,
39367 boxes.push([item]);
39390 var filterPattern = function(box, length)
39398 var pattern = box.slice(0, length);
39402 Roo.each(pattern, function(i){
39403 format.push(i.size);
39406 Roo.each(standard, function(s){
39408 if(String(s) != String(format)){
39417 if(!match && length == 1){
39422 filterPattern(box, length - 1);
39426 queue.push(pattern);
39428 box = box.slice(length, box.length);
39430 filterPattern(box, 4);
39436 Roo.each(boxes, function(box, k){
39442 if(box.length == 1){
39447 filterPattern(box, 4);
39451 this._processVerticalLayoutQueue( queue, isInstant );
39455 // _verticalAlternativeLayoutItems : function( items , isInstant )
39457 // if ( !items || !items.length ) {
39461 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
39465 _horizontalLayoutItems : function ( items , isInstant)
39467 if ( !items || !items.length || items.length < 3) {
39473 var eItems = items.slice(0, 3);
39475 items = items.slice(3, items.length);
39478 ['xs', 'xs', 'xs', 'wide'],
39479 ['xs', 'xs', 'wide'],
39480 ['xs', 'xs', 'sm'],
39481 ['xs', 'xs', 'xs'],
39487 ['sm', 'xs', 'xs'],
39491 ['wide', 'xs', 'xs', 'xs'],
39492 ['wide', 'xs', 'xs'],
39505 Roo.each(items, function(item, k){
39507 switch (item.size) {
39518 boxes.push([item]);
39542 var filterPattern = function(box, length)
39550 var pattern = box.slice(0, length);
39554 Roo.each(pattern, function(i){
39555 format.push(i.size);
39558 Roo.each(standard, function(s){
39560 if(String(s) != String(format)){
39569 if(!match && length == 1){
39574 filterPattern(box, length - 1);
39578 queue.push(pattern);
39580 box = box.slice(length, box.length);
39582 filterPattern(box, 4);
39588 Roo.each(boxes, function(box, k){
39594 if(box.length == 1){
39599 filterPattern(box, 4);
39606 var pos = this.el.getBox(true);
39610 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39612 var hit_end = false;
39614 Roo.each(queue, function(box){
39618 Roo.each(box, function(b){
39620 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39630 Roo.each(box, function(b){
39632 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39635 mx = Math.max(mx, b.x);
39639 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39643 Roo.each(box, function(b){
39645 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39659 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39662 /** Sets position of item in DOM
39663 * @param {Element} item
39664 * @param {Number} x - horizontal position
39665 * @param {Number} y - vertical position
39666 * @param {Boolean} isInstant - disables transitions
39668 _processVerticalLayoutQueue : function( queue, isInstant )
39670 var pos = this.el.getBox(true);
39675 for (var i = 0; i < this.cols; i++){
39679 Roo.each(queue, function(box, k){
39681 var col = k % this.cols;
39683 Roo.each(box, function(b,kk){
39685 b.el.position('absolute');
39687 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39688 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39690 if(b.size == 'md-left' || b.size == 'md-right'){
39691 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39692 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39695 b.el.setWidth(width);
39696 b.el.setHeight(height);
39698 b.el.select('iframe',true).setSize(width,height);
39702 for (var i = 0; i < this.cols; i++){
39704 if(maxY[i] < maxY[col]){
39709 col = Math.min(col, i);
39713 x = pos.x + col * (this.colWidth + this.padWidth);
39717 var positions = [];
39719 switch (box.length){
39721 positions = this.getVerticalOneBoxColPositions(x, y, box);
39724 positions = this.getVerticalTwoBoxColPositions(x, y, box);
39727 positions = this.getVerticalThreeBoxColPositions(x, y, box);
39730 positions = this.getVerticalFourBoxColPositions(x, y, box);
39736 Roo.each(box, function(b,kk){
39738 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39740 var sz = b.el.getSize();
39742 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39750 for (var i = 0; i < this.cols; i++){
39751 mY = Math.max(mY, maxY[i]);
39754 this.el.setHeight(mY - pos.y);
39758 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39760 // var pos = this.el.getBox(true);
39763 // var maxX = pos.right;
39765 // var maxHeight = 0;
39767 // Roo.each(items, function(item, k){
39771 // item.el.position('absolute');
39773 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39775 // item.el.setWidth(width);
39777 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39779 // item.el.setHeight(height);
39782 // item.el.setXY([x, y], isInstant ? false : true);
39784 // item.el.setXY([maxX - width, y], isInstant ? false : true);
39787 // y = y + height + this.alternativePadWidth;
39789 // maxHeight = maxHeight + height + this.alternativePadWidth;
39793 // this.el.setHeight(maxHeight);
39797 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39799 var pos = this.el.getBox(true);
39804 var maxX = pos.right;
39806 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39808 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39810 Roo.each(queue, function(box, k){
39812 Roo.each(box, function(b, kk){
39814 b.el.position('absolute');
39816 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39817 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39819 if(b.size == 'md-left' || b.size == 'md-right'){
39820 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39821 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39824 b.el.setWidth(width);
39825 b.el.setHeight(height);
39833 var positions = [];
39835 switch (box.length){
39837 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39840 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39843 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39846 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39852 Roo.each(box, function(b,kk){
39854 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39856 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39864 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39866 Roo.each(eItems, function(b,k){
39868 b.size = (k == 0) ? 'sm' : 'xs';
39869 b.x = (k == 0) ? 2 : 1;
39870 b.y = (k == 0) ? 2 : 1;
39872 b.el.position('absolute');
39874 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39876 b.el.setWidth(width);
39878 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39880 b.el.setHeight(height);
39884 var positions = [];
39887 x : maxX - this.unitWidth * 2 - this.gutter,
39892 x : maxX - this.unitWidth,
39893 y : minY + (this.unitWidth + this.gutter) * 2
39897 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39901 Roo.each(eItems, function(b,k){
39903 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39909 getVerticalOneBoxColPositions : function(x, y, box)
39913 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39915 if(box[0].size == 'md-left'){
39919 if(box[0].size == 'md-right'){
39924 x : x + (this.unitWidth + this.gutter) * rand,
39931 getVerticalTwoBoxColPositions : function(x, y, box)
39935 if(box[0].size == 'xs'){
39939 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39943 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39957 x : x + (this.unitWidth + this.gutter) * 2,
39958 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39965 getVerticalThreeBoxColPositions : function(x, y, box)
39969 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39977 x : x + (this.unitWidth + this.gutter) * 1,
39982 x : x + (this.unitWidth + this.gutter) * 2,
39990 if(box[0].size == 'xs' && box[1].size == 'xs'){
39999 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
40003 x : x + (this.unitWidth + this.gutter) * 1,
40017 x : x + (this.unitWidth + this.gutter) * 2,
40022 x : x + (this.unitWidth + this.gutter) * 2,
40023 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
40030 getVerticalFourBoxColPositions : function(x, y, box)
40034 if(box[0].size == 'xs'){
40043 y : y + (this.unitHeight + this.gutter) * 1
40048 y : y + (this.unitHeight + this.gutter) * 2
40052 x : x + (this.unitWidth + this.gutter) * 1,
40066 x : x + (this.unitWidth + this.gutter) * 2,
40071 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
40072 y : y + (this.unitHeight + this.gutter) * 1
40076 x : x + (this.unitWidth + this.gutter) * 2,
40077 y : y + (this.unitWidth + this.gutter) * 2
40084 getHorizontalOneBoxColPositions : function(maxX, minY, box)
40088 if(box[0].size == 'md-left'){
40090 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40097 if(box[0].size == 'md-right'){
40099 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
40100 y : minY + (this.unitWidth + this.gutter) * 1
40106 var rand = Math.floor(Math.random() * (4 - box[0].y));
40109 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40110 y : minY + (this.unitWidth + this.gutter) * rand
40117 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
40121 if(box[0].size == 'xs'){
40124 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40129 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40130 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
40138 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40143 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40144 y : minY + (this.unitWidth + this.gutter) * 2
40151 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
40155 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
40158 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40163 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40164 y : minY + (this.unitWidth + this.gutter) * 1
40168 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40169 y : minY + (this.unitWidth + this.gutter) * 2
40176 if(box[0].size == 'xs' && box[1].size == 'xs'){
40179 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40184 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40189 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40190 y : minY + (this.unitWidth + this.gutter) * 1
40198 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40203 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40204 y : minY + (this.unitWidth + this.gutter) * 2
40208 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40209 y : minY + (this.unitWidth + this.gutter) * 2
40216 getHorizontalFourBoxColPositions : function(maxX, minY, box)
40220 if(box[0].size == 'xs'){
40223 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40228 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40233 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),
40238 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
40239 y : minY + (this.unitWidth + this.gutter) * 1
40247 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
40252 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
40253 y : minY + (this.unitWidth + this.gutter) * 2
40257 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
40258 y : minY + (this.unitWidth + this.gutter) * 2
40262 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),
40263 y : minY + (this.unitWidth + this.gutter) * 2
40271 * remove a Masonry Brick
40272 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
40274 removeBrick : function(brick_id)
40280 for (var i = 0; i<this.bricks.length; i++) {
40281 if (this.bricks[i].id == brick_id) {
40282 this.bricks.splice(i,1);
40283 this.el.dom.removeChild(Roo.get(brick_id).dom);
40290 * adds a Masonry Brick
40291 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40293 addBrick : function(cfg)
40295 var cn = new Roo.bootstrap.MasonryBrick(cfg);
40296 //this.register(cn);
40297 cn.parentId = this.id;
40298 cn.render(this.el);
40303 * register a Masonry Brick
40304 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40307 register : function(brick)
40309 this.bricks.push(brick);
40310 brick.masonryId = this.id;
40314 * clear all the Masonry Brick
40316 clearAll : function()
40319 //this.getChildContainer().dom.innerHTML = "";
40320 this.el.dom.innerHTML = '';
40323 getSelected : function()
40325 if (!this.selectedBrick) {
40329 return this.selectedBrick;
40333 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40337 * register a Masonry Layout
40338 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40341 register : function(layout)
40343 this.groups[layout.id] = layout;
40346 * fetch a Masonry Layout based on the masonry layout ID
40347 * @param {string} the masonry layout to add
40348 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40351 get: function(layout_id) {
40352 if (typeof(this.groups[layout_id]) == 'undefined') {
40355 return this.groups[layout_id] ;
40367 * http://masonry.desandro.com
40369 * The idea is to render all the bricks based on vertical width...
40371 * The original code extends 'outlayer' - we might need to use that....
40377 * @class Roo.bootstrap.LayoutMasonryAuto
40378 * @extends Roo.bootstrap.Component
40379 * Bootstrap Layout Masonry class
40382 * Create a new Element
40383 * @param {Object} config The config object
40386 Roo.bootstrap.LayoutMasonryAuto = function(config){
40387 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40390 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
40393 * @cfg {Boolean} isFitWidth - resize the width..
40395 isFitWidth : false, // options..
40397 * @cfg {Boolean} isOriginLeft = left align?
40399 isOriginLeft : true,
40401 * @cfg {Boolean} isOriginTop = top align?
40403 isOriginTop : false,
40405 * @cfg {Boolean} isLayoutInstant = no animation?
40407 isLayoutInstant : false, // needed?
40409 * @cfg {Boolean} isResizingContainer = not sure if this is used..
40411 isResizingContainer : true,
40413 * @cfg {Number} columnWidth width of the columns
40419 * @cfg {Number} maxCols maximum number of columns
40424 * @cfg {Number} padHeight padding below box..
40430 * @cfg {Boolean} isAutoInitial defalut true
40433 isAutoInitial : true,
40439 initialColumnWidth : 0,
40440 currentSize : null,
40442 colYs : null, // array.
40449 bricks: null, //CompositeElement
40450 cols : 0, // array?
40451 // element : null, // wrapped now this.el
40452 _isLayoutInited : null,
40455 getAutoCreate : function(){
40459 cls: 'blog-masonary-wrapper ' + this.cls,
40461 cls : 'mas-boxes masonary'
40468 getChildContainer: function( )
40470 if (this.boxesEl) {
40471 return this.boxesEl;
40474 this.boxesEl = this.el.select('.mas-boxes').first();
40476 return this.boxesEl;
40480 initEvents : function()
40484 if(this.isAutoInitial){
40485 Roo.log('hook children rendered');
40486 this.on('childrenrendered', function() {
40487 Roo.log('children rendered');
40494 initial : function()
40496 this.reloadItems();
40498 this.currentSize = this.el.getBox(true);
40500 /// was window resize... - let's see if this works..
40501 Roo.EventManager.onWindowResize(this.resize, this);
40503 if(!this.isAutoInitial){
40508 this.layout.defer(500,this);
40511 reloadItems: function()
40513 this.bricks = this.el.select('.masonry-brick', true);
40515 this.bricks.each(function(b) {
40516 //Roo.log(b.getSize());
40517 if (!b.attr('originalwidth')) {
40518 b.attr('originalwidth', b.getSize().width);
40523 Roo.log(this.bricks.elements.length);
40526 resize : function()
40529 var cs = this.el.getBox(true);
40531 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40532 Roo.log("no change in with or X");
40535 this.currentSize = cs;
40539 layout : function()
40542 this._resetLayout();
40543 //this._manageStamps();
40545 // don't animate first layout
40546 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40547 this.layoutItems( isInstant );
40549 // flag for initalized
40550 this._isLayoutInited = true;
40553 layoutItems : function( isInstant )
40555 //var items = this._getItemsForLayout( this.items );
40556 // original code supports filtering layout items.. we just ignore it..
40558 this._layoutItems( this.bricks , isInstant );
40560 this._postLayout();
40562 _layoutItems : function ( items , isInstant)
40564 //this.fireEvent( 'layout', this, items );
40567 if ( !items || !items.elements.length ) {
40568 // no items, emit event with empty array
40573 items.each(function(item) {
40574 Roo.log("layout item");
40576 // get x/y object from method
40577 var position = this._getItemLayoutPosition( item );
40579 position.item = item;
40580 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40581 queue.push( position );
40584 this._processLayoutQueue( queue );
40586 /** Sets position of item in DOM
40587 * @param {Element} item
40588 * @param {Number} x - horizontal position
40589 * @param {Number} y - vertical position
40590 * @param {Boolean} isInstant - disables transitions
40592 _processLayoutQueue : function( queue )
40594 for ( var i=0, len = queue.length; i < len; i++ ) {
40595 var obj = queue[i];
40596 obj.item.position('absolute');
40597 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40603 * Any logic you want to do after each layout,
40604 * i.e. size the container
40606 _postLayout : function()
40608 this.resizeContainer();
40611 resizeContainer : function()
40613 if ( !this.isResizingContainer ) {
40616 var size = this._getContainerSize();
40618 this.el.setSize(size.width,size.height);
40619 this.boxesEl.setSize(size.width,size.height);
40625 _resetLayout : function()
40627 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40628 this.colWidth = this.el.getWidth();
40629 //this.gutter = this.el.getWidth();
40631 this.measureColumns();
40637 this.colYs.push( 0 );
40643 measureColumns : function()
40645 this.getContainerWidth();
40646 // if columnWidth is 0, default to outerWidth of first item
40647 if ( !this.columnWidth ) {
40648 var firstItem = this.bricks.first();
40649 Roo.log(firstItem);
40650 this.columnWidth = this.containerWidth;
40651 if (firstItem && firstItem.attr('originalwidth') ) {
40652 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40654 // columnWidth fall back to item of first element
40655 Roo.log("set column width?");
40656 this.initialColumnWidth = this.columnWidth ;
40658 // if first elem has no width, default to size of container
40663 if (this.initialColumnWidth) {
40664 this.columnWidth = this.initialColumnWidth;
40669 // column width is fixed at the top - however if container width get's smaller we should
40672 // this bit calcs how man columns..
40674 var columnWidth = this.columnWidth += this.gutter;
40676 // calculate columns
40677 var containerWidth = this.containerWidth + this.gutter;
40679 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40680 // fix rounding errors, typically with gutters
40681 var excess = columnWidth - containerWidth % columnWidth;
40684 // if overshoot is less than a pixel, round up, otherwise floor it
40685 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40686 cols = Math[ mathMethod ]( cols );
40687 this.cols = Math.max( cols, 1 );
40688 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40690 // padding positioning..
40691 var totalColWidth = this.cols * this.columnWidth;
40692 var padavail = this.containerWidth - totalColWidth;
40693 // so for 2 columns - we need 3 'pads'
40695 var padNeeded = (1+this.cols) * this.padWidth;
40697 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40699 this.columnWidth += padExtra
40700 //this.padWidth = Math.floor(padavail / ( this.cols));
40702 // adjust colum width so that padding is fixed??
40704 // we have 3 columns ... total = width * 3
40705 // we have X left over... that should be used by
40707 //if (this.expandC) {
40715 getContainerWidth : function()
40717 /* // container is parent if fit width
40718 var container = this.isFitWidth ? this.element.parentNode : this.element;
40719 // check that this.size and size are there
40720 // IE8 triggers resize on body size change, so they might not be
40722 var size = getSize( container ); //FIXME
40723 this.containerWidth = size && size.innerWidth; //FIXME
40726 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
40730 _getItemLayoutPosition : function( item ) // what is item?
40732 // we resize the item to our columnWidth..
40734 item.setWidth(this.columnWidth);
40735 item.autoBoxAdjust = false;
40737 var sz = item.getSize();
40739 // how many columns does this brick span
40740 var remainder = this.containerWidth % this.columnWidth;
40742 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40743 // round if off by 1 pixel, otherwise use ceil
40744 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
40745 colSpan = Math.min( colSpan, this.cols );
40747 // normally this should be '1' as we dont' currently allow multi width columns..
40749 var colGroup = this._getColGroup( colSpan );
40750 // get the minimum Y value from the columns
40751 var minimumY = Math.min.apply( Math, colGroup );
40752 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40754 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
40756 // position the brick
40758 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40759 y: this.currentSize.y + minimumY + this.padHeight
40763 // apply setHeight to necessary columns
40764 var setHeight = minimumY + sz.height + this.padHeight;
40765 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40767 var setSpan = this.cols + 1 - colGroup.length;
40768 for ( var i = 0; i < setSpan; i++ ) {
40769 this.colYs[ shortColIndex + i ] = setHeight ;
40776 * @param {Number} colSpan - number of columns the element spans
40777 * @returns {Array} colGroup
40779 _getColGroup : function( colSpan )
40781 if ( colSpan < 2 ) {
40782 // if brick spans only one column, use all the column Ys
40787 // how many different places could this brick fit horizontally
40788 var groupCount = this.cols + 1 - colSpan;
40789 // for each group potential horizontal position
40790 for ( var i = 0; i < groupCount; i++ ) {
40791 // make an array of colY values for that one group
40792 var groupColYs = this.colYs.slice( i, i + colSpan );
40793 // and get the max value of the array
40794 colGroup[i] = Math.max.apply( Math, groupColYs );
40799 _manageStamp : function( stamp )
40801 var stampSize = stamp.getSize();
40802 var offset = stamp.getBox();
40803 // get the columns that this stamp affects
40804 var firstX = this.isOriginLeft ? offset.x : offset.right;
40805 var lastX = firstX + stampSize.width;
40806 var firstCol = Math.floor( firstX / this.columnWidth );
40807 firstCol = Math.max( 0, firstCol );
40809 var lastCol = Math.floor( lastX / this.columnWidth );
40810 // lastCol should not go over if multiple of columnWidth #425
40811 lastCol -= lastX % this.columnWidth ? 0 : 1;
40812 lastCol = Math.min( this.cols - 1, lastCol );
40814 // set colYs to bottom of the stamp
40815 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40818 for ( var i = firstCol; i <= lastCol; i++ ) {
40819 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40824 _getContainerSize : function()
40826 this.maxY = Math.max.apply( Math, this.colYs );
40831 if ( this.isFitWidth ) {
40832 size.width = this._getContainerFitWidth();
40838 _getContainerFitWidth : function()
40840 var unusedCols = 0;
40841 // count unused columns
40844 if ( this.colYs[i] !== 0 ) {
40849 // fit container to columns that have been used
40850 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40853 needsResizeLayout : function()
40855 var previousWidth = this.containerWidth;
40856 this.getContainerWidth();
40857 return previousWidth !== this.containerWidth;
40872 * @class Roo.bootstrap.MasonryBrick
40873 * @extends Roo.bootstrap.Component
40874 * Bootstrap MasonryBrick class
40877 * Create a new MasonryBrick
40878 * @param {Object} config The config object
40881 Roo.bootstrap.MasonryBrick = function(config){
40883 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40885 Roo.bootstrap.MasonryBrick.register(this);
40891 * When a MasonryBrick is clcik
40892 * @param {Roo.bootstrap.MasonryBrick} this
40893 * @param {Roo.EventObject} e
40899 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40902 * @cfg {String} title
40906 * @cfg {String} html
40910 * @cfg {String} bgimage
40914 * @cfg {String} videourl
40918 * @cfg {String} cls
40922 * @cfg {String} href
40926 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40931 * @cfg {String} placetitle (center|bottom)
40936 * @cfg {Boolean} isFitContainer defalut true
40938 isFitContainer : true,
40941 * @cfg {Boolean} preventDefault defalut false
40943 preventDefault : false,
40946 * @cfg {Boolean} inverse defalut false
40948 maskInverse : false,
40950 getAutoCreate : function()
40952 if(!this.isFitContainer){
40953 return this.getSplitAutoCreate();
40956 var cls = 'masonry-brick masonry-brick-full';
40958 if(this.href.length){
40959 cls += ' masonry-brick-link';
40962 if(this.bgimage.length){
40963 cls += ' masonry-brick-image';
40966 if(this.maskInverse){
40967 cls += ' mask-inverse';
40970 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40971 cls += ' enable-mask';
40975 cls += ' masonry-' + this.size + '-brick';
40978 if(this.placetitle.length){
40980 switch (this.placetitle) {
40982 cls += ' masonry-center-title';
40985 cls += ' masonry-bottom-title';
40992 if(!this.html.length && !this.bgimage.length){
40993 cls += ' masonry-center-title';
40996 if(!this.html.length && this.bgimage.length){
40997 cls += ' masonry-bottom-title';
41002 cls += ' ' + this.cls;
41006 tag: (this.href.length) ? 'a' : 'div',
41011 cls: 'masonry-brick-mask'
41015 cls: 'masonry-brick-paragraph',
41021 if(this.href.length){
41022 cfg.href = this.href;
41025 var cn = cfg.cn[1].cn;
41027 if(this.title.length){
41030 cls: 'masonry-brick-title',
41035 if(this.html.length){
41038 cls: 'masonry-brick-text',
41043 if (!this.title.length && !this.html.length) {
41044 cfg.cn[1].cls += ' hide';
41047 if(this.bgimage.length){
41050 cls: 'masonry-brick-image-view',
41055 if(this.videourl.length){
41056 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41057 // youtube support only?
41060 cls: 'masonry-brick-image-view',
41063 allowfullscreen : true
41071 getSplitAutoCreate : function()
41073 var cls = 'masonry-brick masonry-brick-split';
41075 if(this.href.length){
41076 cls += ' masonry-brick-link';
41079 if(this.bgimage.length){
41080 cls += ' masonry-brick-image';
41084 cls += ' masonry-' + this.size + '-brick';
41087 switch (this.placetitle) {
41089 cls += ' masonry-center-title';
41092 cls += ' masonry-bottom-title';
41095 if(!this.bgimage.length){
41096 cls += ' masonry-center-title';
41099 if(this.bgimage.length){
41100 cls += ' masonry-bottom-title';
41106 cls += ' ' + this.cls;
41110 tag: (this.href.length) ? 'a' : 'div',
41115 cls: 'masonry-brick-split-head',
41119 cls: 'masonry-brick-paragraph',
41126 cls: 'masonry-brick-split-body',
41132 if(this.href.length){
41133 cfg.href = this.href;
41136 if(this.title.length){
41137 cfg.cn[0].cn[0].cn.push({
41139 cls: 'masonry-brick-title',
41144 if(this.html.length){
41145 cfg.cn[1].cn.push({
41147 cls: 'masonry-brick-text',
41152 if(this.bgimage.length){
41153 cfg.cn[0].cn.push({
41155 cls: 'masonry-brick-image-view',
41160 if(this.videourl.length){
41161 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
41162 // youtube support only?
41163 cfg.cn[0].cn.cn.push({
41165 cls: 'masonry-brick-image-view',
41168 allowfullscreen : true
41175 initEvents: function()
41177 switch (this.size) {
41210 this.el.on('touchstart', this.onTouchStart, this);
41211 this.el.on('touchmove', this.onTouchMove, this);
41212 this.el.on('touchend', this.onTouchEnd, this);
41213 this.el.on('contextmenu', this.onContextMenu, this);
41215 this.el.on('mouseenter' ,this.enter, this);
41216 this.el.on('mouseleave', this.leave, this);
41217 this.el.on('click', this.onClick, this);
41220 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
41221 this.parent().bricks.push(this);
41226 onClick: function(e, el)
41228 var time = this.endTimer - this.startTimer;
41229 // Roo.log(e.preventDefault());
41232 e.preventDefault();
41237 if(!this.preventDefault){
41241 e.preventDefault();
41243 if (this.activeClass != '') {
41244 this.selectBrick();
41247 this.fireEvent('click', this, e);
41250 enter: function(e, el)
41252 e.preventDefault();
41254 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41258 if(this.bgimage.length && this.html.length){
41259 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41263 leave: function(e, el)
41265 e.preventDefault();
41267 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
41271 if(this.bgimage.length && this.html.length){
41272 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41276 onTouchStart: function(e, el)
41278 // e.preventDefault();
41280 this.touchmoved = false;
41282 if(!this.isFitContainer){
41286 if(!this.bgimage.length || !this.html.length){
41290 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41292 this.timer = new Date().getTime();
41296 onTouchMove: function(e, el)
41298 this.touchmoved = true;
41301 onContextMenu : function(e,el)
41303 e.preventDefault();
41304 e.stopPropagation();
41308 onTouchEnd: function(e, el)
41310 // e.preventDefault();
41312 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41319 if(!this.bgimage.length || !this.html.length){
41321 if(this.href.length){
41322 window.location.href = this.href;
41328 if(!this.isFitContainer){
41332 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41334 window.location.href = this.href;
41337 //selection on single brick only
41338 selectBrick : function() {
41340 if (!this.parentId) {
41344 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41345 var index = m.selectedBrick.indexOf(this.id);
41348 m.selectedBrick.splice(index,1);
41349 this.el.removeClass(this.activeClass);
41353 for(var i = 0; i < m.selectedBrick.length; i++) {
41354 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41355 b.el.removeClass(b.activeClass);
41358 m.selectedBrick = [];
41360 m.selectedBrick.push(this.id);
41361 this.el.addClass(this.activeClass);
41365 isSelected : function(){
41366 return this.el.hasClass(this.activeClass);
41371 Roo.apply(Roo.bootstrap.MasonryBrick, {
41374 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41376 * register a Masonry Brick
41377 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41380 register : function(brick)
41382 //this.groups[brick.id] = brick;
41383 this.groups.add(brick.id, brick);
41386 * fetch a masonry brick based on the masonry brick ID
41387 * @param {string} the masonry brick to add
41388 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41391 get: function(brick_id)
41393 // if (typeof(this.groups[brick_id]) == 'undefined') {
41396 // return this.groups[brick_id] ;
41398 if(this.groups.key(brick_id)) {
41399 return this.groups.key(brick_id);
41417 * @class Roo.bootstrap.Brick
41418 * @extends Roo.bootstrap.Component
41419 * Bootstrap Brick class
41422 * Create a new Brick
41423 * @param {Object} config The config object
41426 Roo.bootstrap.Brick = function(config){
41427 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41433 * When a Brick is click
41434 * @param {Roo.bootstrap.Brick} this
41435 * @param {Roo.EventObject} e
41441 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
41444 * @cfg {String} title
41448 * @cfg {String} html
41452 * @cfg {String} bgimage
41456 * @cfg {String} cls
41460 * @cfg {String} href
41464 * @cfg {String} video
41468 * @cfg {Boolean} square
41472 getAutoCreate : function()
41474 var cls = 'roo-brick';
41476 if(this.href.length){
41477 cls += ' roo-brick-link';
41480 if(this.bgimage.length){
41481 cls += ' roo-brick-image';
41484 if(!this.html.length && !this.bgimage.length){
41485 cls += ' roo-brick-center-title';
41488 if(!this.html.length && this.bgimage.length){
41489 cls += ' roo-brick-bottom-title';
41493 cls += ' ' + this.cls;
41497 tag: (this.href.length) ? 'a' : 'div',
41502 cls: 'roo-brick-paragraph',
41508 if(this.href.length){
41509 cfg.href = this.href;
41512 var cn = cfg.cn[0].cn;
41514 if(this.title.length){
41517 cls: 'roo-brick-title',
41522 if(this.html.length){
41525 cls: 'roo-brick-text',
41532 if(this.bgimage.length){
41535 cls: 'roo-brick-image-view',
41543 initEvents: function()
41545 if(this.title.length || this.html.length){
41546 this.el.on('mouseenter' ,this.enter, this);
41547 this.el.on('mouseleave', this.leave, this);
41550 Roo.EventManager.onWindowResize(this.resize, this);
41552 if(this.bgimage.length){
41553 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41554 this.imageEl.on('load', this.onImageLoad, this);
41561 onImageLoad : function()
41566 resize : function()
41568 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41570 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41572 if(this.bgimage.length){
41573 var image = this.el.select('.roo-brick-image-view', true).first();
41575 image.setWidth(paragraph.getWidth());
41578 image.setHeight(paragraph.getWidth());
41581 this.el.setHeight(image.getHeight());
41582 paragraph.setHeight(image.getHeight());
41588 enter: function(e, el)
41590 e.preventDefault();
41592 if(this.bgimage.length){
41593 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41594 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41598 leave: function(e, el)
41600 e.preventDefault();
41602 if(this.bgimage.length){
41603 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41604 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41619 * @class Roo.bootstrap.form.NumberField
41620 * @extends Roo.bootstrap.form.Input
41621 * Bootstrap NumberField class
41627 * Create a new NumberField
41628 * @param {Object} config The config object
41631 Roo.bootstrap.form.NumberField = function(config){
41632 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41635 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41638 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41640 allowDecimals : true,
41642 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41644 decimalSeparator : ".",
41646 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41648 decimalPrecision : 2,
41650 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41652 allowNegative : true,
41655 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41659 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41661 minValue : Number.NEGATIVE_INFINITY,
41663 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41665 maxValue : Number.MAX_VALUE,
41667 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41669 minText : "The minimum value for this field is {0}",
41671 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41673 maxText : "The maximum value for this field is {0}",
41675 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41676 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41678 nanText : "{0} is not a valid number",
41680 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41682 thousandsDelimiter : false,
41684 * @cfg {String} valueAlign alignment of value
41686 valueAlign : "left",
41688 getAutoCreate : function()
41690 var hiddenInput = {
41694 cls: 'hidden-number-input'
41698 hiddenInput.name = this.name;
41703 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41705 this.name = hiddenInput.name;
41707 if(cfg.cn.length > 0) {
41708 cfg.cn.push(hiddenInput);
41715 initEvents : function()
41717 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41719 var allowed = "0123456789";
41721 if(this.allowDecimals){
41722 allowed += this.decimalSeparator;
41725 if(this.allowNegative){
41729 if(this.thousandsDelimiter) {
41733 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41735 var keyPress = function(e){
41737 var k = e.getKey();
41739 var c = e.getCharCode();
41742 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41743 allowed.indexOf(String.fromCharCode(c)) === -1
41749 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41753 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41758 this.el.on("keypress", keyPress, this);
41761 validateValue : function(value)
41764 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41768 var num = this.parseValue(value);
41771 this.markInvalid(String.format(this.nanText, value));
41775 if(num < this.minValue){
41776 this.markInvalid(String.format(this.minText, this.minValue));
41780 if(num > this.maxValue){
41781 this.markInvalid(String.format(this.maxText, this.maxValue));
41788 getValue : function()
41790 var v = this.hiddenEl().getValue();
41792 return this.fixPrecision(this.parseValue(v));
41795 parseValue : function(value)
41797 if(this.thousandsDelimiter) {
41799 r = new RegExp(",", "g");
41800 value = value.replace(r, "");
41803 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41804 return isNaN(value) ? '' : value;
41807 fixPrecision : function(value)
41809 if(this.thousandsDelimiter) {
41811 r = new RegExp(",", "g");
41812 value = value.replace(r, "");
41815 var nan = isNaN(value);
41817 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41818 return nan ? '' : value;
41820 return parseFloat(value).toFixed(this.decimalPrecision);
41823 setValue : function(v)
41825 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41831 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41833 this.inputEl().dom.value = (v == '') ? '' :
41834 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41836 if(!this.allowZero && v === '0') {
41837 this.hiddenEl().dom.value = '';
41838 this.inputEl().dom.value = '';
41845 decimalPrecisionFcn : function(v)
41847 return Math.floor(v);
41850 beforeBlur : function()
41852 var v = this.parseValue(this.getRawValue());
41854 if(v || v === 0 || v === ''){
41859 hiddenEl : function()
41861 return this.el.select('input.hidden-number-input',true).first();
41873 * @class Roo.bootstrap.DocumentSlider
41874 * @extends Roo.bootstrap.Component
41875 * Bootstrap DocumentSlider class
41878 * Create a new DocumentViewer
41879 * @param {Object} config The config object
41882 Roo.bootstrap.DocumentSlider = function(config){
41883 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41890 * Fire after initEvent
41891 * @param {Roo.bootstrap.DocumentSlider} this
41896 * Fire after update
41897 * @param {Roo.bootstrap.DocumentSlider} this
41903 * @param {Roo.bootstrap.DocumentSlider} this
41909 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41915 getAutoCreate : function()
41919 cls : 'roo-document-slider',
41923 cls : 'roo-document-slider-header',
41927 cls : 'roo-document-slider-header-title'
41933 cls : 'roo-document-slider-body',
41937 cls : 'roo-document-slider-prev',
41941 cls : 'fa fa-chevron-left'
41947 cls : 'roo-document-slider-thumb',
41951 cls : 'roo-document-slider-image'
41957 cls : 'roo-document-slider-next',
41961 cls : 'fa fa-chevron-right'
41973 initEvents : function()
41975 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41976 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41978 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41979 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41981 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41982 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41984 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41985 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41987 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41988 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41990 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41991 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41993 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41994 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41996 this.thumbEl.on('click', this.onClick, this);
41998 this.prevIndicator.on('click', this.prev, this);
42000 this.nextIndicator.on('click', this.next, this);
42004 initial : function()
42006 if(this.files.length){
42007 this.indicator = 1;
42011 this.fireEvent('initial', this);
42014 update : function()
42016 this.imageEl.attr('src', this.files[this.indicator - 1]);
42018 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
42020 this.prevIndicator.show();
42022 if(this.indicator == 1){
42023 this.prevIndicator.hide();
42026 this.nextIndicator.show();
42028 if(this.indicator == this.files.length){
42029 this.nextIndicator.hide();
42032 this.thumbEl.scrollTo('top');
42034 this.fireEvent('update', this);
42037 onClick : function(e)
42039 e.preventDefault();
42041 this.fireEvent('click', this);
42046 e.preventDefault();
42048 this.indicator = Math.max(1, this.indicator - 1);
42055 e.preventDefault();
42057 this.indicator = Math.min(this.files.length, this.indicator + 1);
42071 * @class Roo.bootstrap.form.RadioSet
42072 * @extends Roo.bootstrap.form.Input
42073 * @children Roo.bootstrap.form.Radio
42074 * Bootstrap RadioSet class
42075 * @cfg {String} indicatorpos (left|right) default left
42076 * @cfg {Boolean} inline (true|false) inline the element (default true)
42077 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
42079 * Create a new RadioSet
42080 * @param {Object} config The config object
42083 Roo.bootstrap.form.RadioSet = function(config){
42085 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
42089 Roo.bootstrap.form.RadioSet.register(this);
42094 * Fires when the element is checked or unchecked.
42095 * @param {Roo.bootstrap.form.RadioSet} this This radio
42096 * @param {Roo.bootstrap.form.Radio} item The checked item
42101 * Fires when the element is click.
42102 * @param {Roo.bootstrap.form.RadioSet} this This radio set
42103 * @param {Roo.bootstrap.form.Radio} item The checked item
42104 * @param {Roo.EventObject} e The event object
42111 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
42119 indicatorpos : 'left',
42121 getAutoCreate : function()
42125 cls : 'roo-radio-set-label',
42129 html : this.fieldLabel
42133 if (Roo.bootstrap.version == 3) {
42136 if(this.indicatorpos == 'left'){
42139 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
42140 tooltip : 'This field is required'
42145 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
42146 tooltip : 'This field is required'
42152 cls : 'roo-radio-set-items'
42155 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
42157 if (align === 'left' && this.fieldLabel.length) {
42160 cls : "roo-radio-set-right",
42166 if(this.labelWidth > 12){
42167 label.style = "width: " + this.labelWidth + 'px';
42170 if(this.labelWidth < 13 && this.labelmd == 0){
42171 this.labelmd = this.labelWidth;
42174 if(this.labellg > 0){
42175 label.cls += ' col-lg-' + this.labellg;
42176 items.cls += ' col-lg-' + (12 - this.labellg);
42179 if(this.labelmd > 0){
42180 label.cls += ' col-md-' + this.labelmd;
42181 items.cls += ' col-md-' + (12 - this.labelmd);
42184 if(this.labelsm > 0){
42185 label.cls += ' col-sm-' + this.labelsm;
42186 items.cls += ' col-sm-' + (12 - this.labelsm);
42189 if(this.labelxs > 0){
42190 label.cls += ' col-xs-' + this.labelxs;
42191 items.cls += ' col-xs-' + (12 - this.labelxs);
42197 cls : 'roo-radio-set',
42201 cls : 'roo-radio-set-input',
42204 value : this.value ? this.value : ''
42211 if(this.weight.length){
42212 cfg.cls += ' roo-radio-' + this.weight;
42216 cfg.cls += ' roo-radio-set-inline';
42220 ['xs','sm','md','lg'].map(function(size){
42221 if (settings[size]) {
42222 cfg.cls += ' col-' + size + '-' + settings[size];
42230 initEvents : function()
42232 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
42233 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
42235 if(!this.fieldLabel.length){
42236 this.labelEl.hide();
42239 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
42240 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
42242 this.indicator = this.indicatorEl();
42244 if(this.indicator){
42245 this.indicator.addClass('invisible');
42248 this.originalValue = this.getValue();
42252 inputEl: function ()
42254 return this.el.select('.roo-radio-set-input', true).first();
42257 getChildContainer : function()
42259 return this.itemsEl;
42262 register : function(item)
42264 this.radioes.push(item);
42268 validate : function()
42270 if(this.getVisibilityEl().hasClass('hidden')){
42276 Roo.each(this.radioes, function(i){
42285 if(this.allowBlank) {
42289 if(this.disabled || valid){
42294 this.markInvalid();
42299 markValid : function()
42301 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42302 this.indicatorEl().removeClass('visible');
42303 this.indicatorEl().addClass('invisible');
42307 if (Roo.bootstrap.version == 3) {
42308 this.el.removeClass([this.invalidClass, this.validClass]);
42309 this.el.addClass(this.validClass);
42311 this.el.removeClass(['is-invalid','is-valid']);
42312 this.el.addClass(['is-valid']);
42314 this.fireEvent('valid', this);
42317 markInvalid : function(msg)
42319 if(this.allowBlank || this.disabled){
42323 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42324 this.indicatorEl().removeClass('invisible');
42325 this.indicatorEl().addClass('visible');
42327 if (Roo.bootstrap.version == 3) {
42328 this.el.removeClass([this.invalidClass, this.validClass]);
42329 this.el.addClass(this.invalidClass);
42331 this.el.removeClass(['is-invalid','is-valid']);
42332 this.el.addClass(['is-invalid']);
42335 this.fireEvent('invalid', this, msg);
42339 setValue : function(v, suppressEvent)
42341 if(this.value === v){
42348 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42351 Roo.each(this.radioes, function(i){
42353 i.el.removeClass('checked');
42356 Roo.each(this.radioes, function(i){
42358 if(i.value === v || i.value.toString() === v.toString()){
42360 i.el.addClass('checked');
42362 if(suppressEvent !== true){
42363 this.fireEvent('check', this, i);
42374 clearInvalid : function(){
42376 if(!this.el || this.preventMark){
42380 this.el.removeClass([this.invalidClass]);
42382 this.fireEvent('valid', this);
42387 Roo.apply(Roo.bootstrap.form.RadioSet, {
42391 register : function(set)
42393 this.groups[set.name] = set;
42396 get: function(name)
42398 if (typeof(this.groups[name]) == 'undefined') {
42402 return this.groups[name] ;
42408 * Ext JS Library 1.1.1
42409 * Copyright(c) 2006-2007, Ext JS, LLC.
42411 * Originally Released Under LGPL - original licence link has changed is not relivant.
42414 * <script type="text/javascript">
42419 * @class Roo.bootstrap.SplitBar
42420 * @extends Roo.util.Observable
42421 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42425 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42426 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42427 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42428 split.minSize = 100;
42429 split.maxSize = 600;
42430 split.animate = true;
42431 split.on('moved', splitterMoved);
42434 * Create a new SplitBar
42435 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
42436 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
42437 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42438 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
42439 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42440 position of the SplitBar).
42442 Roo.bootstrap.SplitBar = function(cfg){
42447 // dragElement : elm
42448 // resizingElement: el,
42450 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42451 // placement : Roo.bootstrap.SplitBar.LEFT ,
42452 // existingProxy ???
42455 this.el = Roo.get(cfg.dragElement, true);
42456 this.el.dom.unselectable = "on";
42458 this.resizingEl = Roo.get(cfg.resizingElement, true);
42462 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42463 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42466 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42469 * The minimum size of the resizing element. (Defaults to 0)
42475 * The maximum size of the resizing element. (Defaults to 2000)
42478 this.maxSize = 2000;
42481 * Whether to animate the transition to the new size
42484 this.animate = false;
42487 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42490 this.useShim = false;
42495 if(!cfg.existingProxy){
42497 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42499 this.proxy = Roo.get(cfg.existingProxy).dom;
42502 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42505 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42508 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42511 this.dragSpecs = {};
42514 * @private The adapter to use to positon and resize elements
42516 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42517 this.adapter.init(this);
42519 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42521 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42522 this.el.addClass("roo-splitbar-h");
42525 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42526 this.el.addClass("roo-splitbar-v");
42532 * Fires when the splitter is moved (alias for {@link #event-moved})
42533 * @param {Roo.bootstrap.SplitBar} this
42534 * @param {Number} newSize the new width or height
42539 * Fires when the splitter is moved
42540 * @param {Roo.bootstrap.SplitBar} this
42541 * @param {Number} newSize the new width or height
42545 * @event beforeresize
42546 * Fires before the splitter is dragged
42547 * @param {Roo.bootstrap.SplitBar} this
42549 "beforeresize" : true,
42551 "beforeapply" : true
42554 Roo.util.Observable.call(this);
42557 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42558 onStartProxyDrag : function(x, y){
42559 this.fireEvent("beforeresize", this);
42561 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
42563 o.enableDisplayMode("block");
42564 // all splitbars share the same overlay
42565 Roo.bootstrap.SplitBar.prototype.overlay = o;
42567 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42568 this.overlay.show();
42569 Roo.get(this.proxy).setDisplayed("block");
42570 var size = this.adapter.getElementSize(this);
42571 this.activeMinSize = this.getMinimumSize();;
42572 this.activeMaxSize = this.getMaximumSize();;
42573 var c1 = size - this.activeMinSize;
42574 var c2 = Math.max(this.activeMaxSize - size, 0);
42575 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42576 this.dd.resetConstraints();
42577 this.dd.setXConstraint(
42578 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
42579 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42581 this.dd.setYConstraint(0, 0);
42583 this.dd.resetConstraints();
42584 this.dd.setXConstraint(0, 0);
42585 this.dd.setYConstraint(
42586 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
42587 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42590 this.dragSpecs.startSize = size;
42591 this.dragSpecs.startPoint = [x, y];
42592 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42596 * @private Called after the drag operation by the DDProxy
42598 onEndProxyDrag : function(e){
42599 Roo.get(this.proxy).setDisplayed(false);
42600 var endPoint = Roo.lib.Event.getXY(e);
42602 this.overlay.hide();
42605 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42606 newSize = this.dragSpecs.startSize +
42607 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42608 endPoint[0] - this.dragSpecs.startPoint[0] :
42609 this.dragSpecs.startPoint[0] - endPoint[0]
42612 newSize = this.dragSpecs.startSize +
42613 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42614 endPoint[1] - this.dragSpecs.startPoint[1] :
42615 this.dragSpecs.startPoint[1] - endPoint[1]
42618 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42619 if(newSize != this.dragSpecs.startSize){
42620 if(this.fireEvent('beforeapply', this, newSize) !== false){
42621 this.adapter.setElementSize(this, newSize);
42622 this.fireEvent("moved", this, newSize);
42623 this.fireEvent("resize", this, newSize);
42629 * Get the adapter this SplitBar uses
42630 * @return The adapter object
42632 getAdapter : function(){
42633 return this.adapter;
42637 * Set the adapter this SplitBar uses
42638 * @param {Object} adapter A SplitBar adapter object
42640 setAdapter : function(adapter){
42641 this.adapter = adapter;
42642 this.adapter.init(this);
42646 * Gets the minimum size for the resizing element
42647 * @return {Number} The minimum size
42649 getMinimumSize : function(){
42650 return this.minSize;
42654 * Sets the minimum size for the resizing element
42655 * @param {Number} minSize The minimum size
42657 setMinimumSize : function(minSize){
42658 this.minSize = minSize;
42662 * Gets the maximum size for the resizing element
42663 * @return {Number} The maximum size
42665 getMaximumSize : function(){
42666 return this.maxSize;
42670 * Sets the maximum size for the resizing element
42671 * @param {Number} maxSize The maximum size
42673 setMaximumSize : function(maxSize){
42674 this.maxSize = maxSize;
42678 * Sets the initialize size for the resizing element
42679 * @param {Number} size The initial size
42681 setCurrentSize : function(size){
42682 var oldAnimate = this.animate;
42683 this.animate = false;
42684 this.adapter.setElementSize(this, size);
42685 this.animate = oldAnimate;
42689 * Destroy this splitbar.
42690 * @param {Boolean} removeEl True to remove the element
42692 destroy : function(removeEl){
42694 this.shim.remove();
42697 this.proxy.parentNode.removeChild(this.proxy);
42705 * @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.
42707 Roo.bootstrap.SplitBar.createProxy = function(dir){
42708 var proxy = new Roo.Element(document.createElement("div"));
42709 proxy.unselectable();
42710 var cls = 'roo-splitbar-proxy';
42711 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42712 document.body.appendChild(proxy.dom);
42717 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42718 * Default Adapter. It assumes the splitter and resizing element are not positioned
42719 * elements and only gets/sets the width of the element. Generally used for table based layouts.
42721 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42724 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42725 // do nothing for now
42726 init : function(s){
42730 * Called before drag operations to get the current size of the resizing element.
42731 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42733 getElementSize : function(s){
42734 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42735 return s.resizingEl.getWidth();
42737 return s.resizingEl.getHeight();
42742 * Called after drag operations to set the size of the resizing element.
42743 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42744 * @param {Number} newSize The new size to set
42745 * @param {Function} onComplete A function to be invoked when resizing is complete
42747 setElementSize : function(s, newSize, onComplete){
42748 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42750 s.resizingEl.setWidth(newSize);
42752 onComplete(s, newSize);
42755 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42760 s.resizingEl.setHeight(newSize);
42762 onComplete(s, newSize);
42765 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42772 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42773 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42774 * Adapter that moves the splitter element to align with the resized sizing element.
42775 * Used with an absolute positioned SplitBar.
42776 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42777 * document.body, make sure you assign an id to the body element.
42779 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42780 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42781 this.container = Roo.get(container);
42784 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42785 init : function(s){
42786 this.basic.init(s);
42789 getElementSize : function(s){
42790 return this.basic.getElementSize(s);
42793 setElementSize : function(s, newSize, onComplete){
42794 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42797 moveSplitter : function(s){
42798 var yes = Roo.bootstrap.SplitBar;
42799 switch(s.placement){
42801 s.el.setX(s.resizingEl.getRight());
42804 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42807 s.el.setY(s.resizingEl.getBottom());
42810 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42817 * Orientation constant - Create a vertical SplitBar
42821 Roo.bootstrap.SplitBar.VERTICAL = 1;
42824 * Orientation constant - Create a horizontal SplitBar
42828 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42831 * Placement constant - The resizing element is to the left of the splitter element
42835 Roo.bootstrap.SplitBar.LEFT = 1;
42838 * Placement constant - The resizing element is to the right of the splitter element
42842 Roo.bootstrap.SplitBar.RIGHT = 2;
42845 * Placement constant - The resizing element is positioned above the splitter element
42849 Roo.bootstrap.SplitBar.TOP = 3;
42852 * Placement constant - The resizing element is positioned under splitter element
42856 Roo.bootstrap.SplitBar.BOTTOM = 4;
42859 * Ext JS Library 1.1.1
42860 * Copyright(c) 2006-2007, Ext JS, LLC.
42862 * Originally Released Under LGPL - original licence link has changed is not relivant.
42865 * <script type="text/javascript">
42869 * @class Roo.bootstrap.layout.Manager
42870 * @extends Roo.bootstrap.Component
42872 * Base class for layout managers.
42874 Roo.bootstrap.layout.Manager = function(config)
42876 this.monitorWindowResize = true; // do this before we apply configuration.
42878 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42884 /** false to disable window resize monitoring @type Boolean */
42890 * Fires when a layout is performed.
42891 * @param {Roo.LayoutManager} this
42895 * @event regionresized
42896 * Fires when the user resizes a region.
42897 * @param {Roo.LayoutRegion} region The resized region
42898 * @param {Number} newSize The new size (width for east/west, height for north/south)
42900 "regionresized" : true,
42902 * @event regioncollapsed
42903 * Fires when a region is collapsed.
42904 * @param {Roo.LayoutRegion} region The collapsed region
42906 "regioncollapsed" : true,
42908 * @event regionexpanded
42909 * Fires when a region is expanded.
42910 * @param {Roo.LayoutRegion} region The expanded region
42912 "regionexpanded" : true
42914 this.updating = false;
42917 this.el = Roo.get(config.el);
42923 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42928 monitorWindowResize : true,
42934 onRender : function(ct, position)
42937 this.el = Roo.get(ct);
42940 //this.fireEvent('render',this);
42944 initEvents: function()
42948 // ie scrollbar fix
42949 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42950 document.body.scroll = "no";
42951 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42952 this.el.position('relative');
42954 this.id = this.el.id;
42955 this.el.addClass("roo-layout-container");
42956 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42957 if(this.el.dom != document.body ) {
42958 this.el.on('resize', this.layout,this);
42959 this.el.on('show', this.layout,this);
42965 * Returns true if this layout is currently being updated
42966 * @return {Boolean}
42968 isUpdating : function(){
42969 return this.updating;
42973 * Suspend the LayoutManager from doing auto-layouts while
42974 * making multiple add or remove calls
42976 beginUpdate : function(){
42977 this.updating = true;
42981 * Restore auto-layouts and optionally disable the manager from performing a layout
42982 * @param {Boolean} noLayout true to disable a layout update
42984 endUpdate : function(noLayout){
42985 this.updating = false;
42991 layout: function(){
42995 onRegionResized : function(region, newSize){
42996 this.fireEvent("regionresized", region, newSize);
43000 onRegionCollapsed : function(region){
43001 this.fireEvent("regioncollapsed", region);
43004 onRegionExpanded : function(region){
43005 this.fireEvent("regionexpanded", region);
43009 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43010 * performs box-model adjustments.
43011 * @return {Object} The size as an object {width: (the width), height: (the height)}
43013 getViewSize : function()
43016 if(this.el.dom != document.body){
43017 size = this.el.getSize();
43019 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43021 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43022 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43027 * Returns the Element this layout is bound to.
43028 * @return {Roo.Element}
43030 getEl : function(){
43035 * Returns the specified region.
43036 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43037 * @return {Roo.LayoutRegion}
43039 getRegion : function(target){
43040 return this.regions[target.toLowerCase()];
43043 onWindowResize : function(){
43044 if(this.monitorWindowResize){
43051 * Ext JS Library 1.1.1
43052 * Copyright(c) 2006-2007, Ext JS, LLC.
43054 * Originally Released Under LGPL - original licence link has changed is not relivant.
43057 * <script type="text/javascript">
43060 * @class Roo.bootstrap.layout.Border
43061 * @extends Roo.bootstrap.layout.Manager
43062 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
43063 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
43064 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43065 * please see: examples/bootstrap/nested.html<br><br>
43067 <b>The container the layout is rendered into can be either the body element or any other element.
43068 If it is not the body element, the container needs to either be an absolute positioned element,
43069 or you will need to add "position:relative" to the css of the container. You will also need to specify
43070 the container size if it is not the body element.</b>
43073 * Create a new Border
43074 * @param {Object} config Configuration options
43076 Roo.bootstrap.layout.Border = function(config){
43077 config = config || {};
43078 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
43082 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43083 if(config[region]){
43084 config[region].region = region;
43085 this.addRegion(config[region]);
43091 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
43093 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
43096 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
43099 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
43102 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
43105 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
43108 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
43114 parent : false, // this might point to a 'nest' or a ???
43117 * Creates and adds a new region if it doesn't already exist.
43118 * @param {String} target The target region key (north, south, east, west or center).
43119 * @param {Object} config The regions config object
43120 * @return {BorderLayoutRegion} The new region
43122 addRegion : function(config)
43124 if(!this.regions[config.region]){
43125 var r = this.factory(config);
43126 this.bindRegion(r);
43128 return this.regions[config.region];
43132 bindRegion : function(r){
43133 this.regions[r.config.region] = r;
43135 r.on("visibilitychange", this.layout, this);
43136 r.on("paneladded", this.layout, this);
43137 r.on("panelremoved", this.layout, this);
43138 r.on("invalidated", this.layout, this);
43139 r.on("resized", this.onRegionResized, this);
43140 r.on("collapsed", this.onRegionCollapsed, this);
43141 r.on("expanded", this.onRegionExpanded, this);
43145 * Performs a layout update.
43147 layout : function()
43149 if(this.updating) {
43153 // render all the rebions if they have not been done alreayd?
43154 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
43155 if(this.regions[region] && !this.regions[region].bodyEl){
43156 this.regions[region].onRender(this.el)
43160 var size = this.getViewSize();
43161 var w = size.width;
43162 var h = size.height;
43167 //var x = 0, y = 0;
43169 var rs = this.regions;
43170 var north = rs["north"];
43171 var south = rs["south"];
43172 var west = rs["west"];
43173 var east = rs["east"];
43174 var center = rs["center"];
43175 //if(this.hideOnLayout){ // not supported anymore
43176 //c.el.setStyle("display", "none");
43178 if(north && north.isVisible()){
43179 var b = north.getBox();
43180 var m = north.getMargins();
43181 b.width = w - (m.left+m.right);
43184 centerY = b.height + b.y + m.bottom;
43185 centerH -= centerY;
43186 north.updateBox(this.safeBox(b));
43188 if(south && south.isVisible()){
43189 var b = south.getBox();
43190 var m = south.getMargins();
43191 b.width = w - (m.left+m.right);
43193 var totalHeight = (b.height + m.top + m.bottom);
43194 b.y = h - totalHeight + m.top;
43195 centerH -= totalHeight;
43196 south.updateBox(this.safeBox(b));
43198 if(west && west.isVisible()){
43199 var b = west.getBox();
43200 var m = west.getMargins();
43201 b.height = centerH - (m.top+m.bottom);
43203 b.y = centerY + m.top;
43204 var totalWidth = (b.width + m.left + m.right);
43205 centerX += totalWidth;
43206 centerW -= totalWidth;
43207 west.updateBox(this.safeBox(b));
43209 if(east && east.isVisible()){
43210 var b = east.getBox();
43211 var m = east.getMargins();
43212 b.height = centerH - (m.top+m.bottom);
43213 var totalWidth = (b.width + m.left + m.right);
43214 b.x = w - totalWidth + m.left;
43215 b.y = centerY + m.top;
43216 centerW -= totalWidth;
43217 east.updateBox(this.safeBox(b));
43220 var m = center.getMargins();
43222 x: centerX + m.left,
43223 y: centerY + m.top,
43224 width: centerW - (m.left+m.right),
43225 height: centerH - (m.top+m.bottom)
43227 //if(this.hideOnLayout){
43228 //center.el.setStyle("display", "block");
43230 center.updateBox(this.safeBox(centerBox));
43233 this.fireEvent("layout", this);
43237 safeBox : function(box){
43238 box.width = Math.max(0, box.width);
43239 box.height = Math.max(0, box.height);
43244 * Adds a ContentPanel (or subclass) to this layout.
43245 * @param {String} target The target region key (north, south, east, west or center).
43246 * @param {Roo.ContentPanel} panel The panel to add
43247 * @return {Roo.ContentPanel} The added panel
43249 add : function(target, panel){
43251 target = target.toLowerCase();
43252 return this.regions[target].add(panel);
43256 * Remove a ContentPanel (or subclass) to this layout.
43257 * @param {String} target The target region key (north, south, east, west or center).
43258 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43259 * @return {Roo.ContentPanel} The removed panel
43261 remove : function(target, panel){
43262 target = target.toLowerCase();
43263 return this.regions[target].remove(panel);
43267 * Searches all regions for a panel with the specified id
43268 * @param {String} panelId
43269 * @return {Roo.ContentPanel} The panel or null if it wasn't found
43271 findPanel : function(panelId){
43272 var rs = this.regions;
43273 for(var target in rs){
43274 if(typeof rs[target] != "function"){
43275 var p = rs[target].getPanel(panelId);
43285 * Searches all regions for a panel with the specified id and activates (shows) it.
43286 * @param {String/ContentPanel} panelId The panels id or the panel itself
43287 * @return {Roo.ContentPanel} The shown panel or null
43289 showPanel : function(panelId) {
43290 var rs = this.regions;
43291 for(var target in rs){
43292 var r = rs[target];
43293 if(typeof r != "function"){
43294 if(r.hasPanel(panelId)){
43295 return r.showPanel(panelId);
43303 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43304 * @param {Roo.state.Provider} provider (optional) An alternate state provider
43307 restoreState : function(provider){
43309 provider = Roo.state.Manager;
43311 var sm = new Roo.LayoutStateManager();
43312 sm.init(this, provider);
43318 * Adds a xtype elements to the layout.
43322 xtype : 'ContentPanel',
43329 xtype : 'NestedLayoutPanel',
43335 items : [ ... list of content panels or nested layout panels.. ]
43339 * @param {Object} cfg Xtype definition of item to add.
43341 addxtype : function(cfg)
43343 // basically accepts a pannel...
43344 // can accept a layout region..!?!?
43345 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43348 // theory? children can only be panels??
43350 //if (!cfg.xtype.match(/Panel$/)) {
43355 if (typeof(cfg.region) == 'undefined') {
43356 Roo.log("Failed to add Panel, region was not set");
43360 var region = cfg.region;
43366 xitems = cfg.items;
43371 if ( region == 'center') {
43372 Roo.log("Center: " + cfg.title);
43378 case 'Content': // ContentPanel (el, cfg)
43379 case 'Scroll': // ContentPanel (el, cfg)
43381 cfg.autoCreate = cfg.autoCreate || true;
43382 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43384 // var el = this.el.createChild();
43385 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43388 this.add(region, ret);
43392 case 'TreePanel': // our new panel!
43393 cfg.el = this.el.createChild();
43394 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43395 this.add(region, ret);
43400 // create a new Layout (which is a Border Layout...
43402 var clayout = cfg.layout;
43403 clayout.el = this.el.createChild();
43404 clayout.items = clayout.items || [];
43408 // replace this exitems with the clayout ones..
43409 xitems = clayout.items;
43411 // force background off if it's in center...
43412 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43413 cfg.background = false;
43415 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
43418 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43419 //console.log('adding nested layout panel ' + cfg.toSource());
43420 this.add(region, ret);
43421 nb = {}; /// find first...
43426 // needs grid and region
43428 //var el = this.getRegion(region).el.createChild();
43430 *var el = this.el.createChild();
43431 // create the grid first...
43432 cfg.grid.container = el;
43433 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43436 if (region == 'center' && this.active ) {
43437 cfg.background = false;
43440 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43442 this.add(region, ret);
43444 if (cfg.background) {
43445 // render grid on panel activation (if panel background)
43446 ret.on('activate', function(gp) {
43447 if (!gp.grid.rendered) {
43448 // gp.grid.render(el);
43452 // cfg.grid.render(el);
43458 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43459 // it was the old xcomponent building that caused this before.
43460 // espeically if border is the top element in the tree.
43470 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43472 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43473 this.add(region, ret);
43477 throw "Can not add '" + cfg.xtype + "' to Border";
43483 this.beginUpdate();
43487 Roo.each(xitems, function(i) {
43488 region = nb && i.region ? i.region : false;
43490 var add = ret.addxtype(i);
43493 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43494 if (!i.background) {
43495 abn[region] = nb[region] ;
43502 // make the last non-background panel active..
43503 //if (nb) { Roo.log(abn); }
43506 for(var r in abn) {
43507 region = this.getRegion(r);
43509 // tried using nb[r], but it does not work..
43511 region.showPanel(abn[r]);
43522 factory : function(cfg)
43525 var validRegions = Roo.bootstrap.layout.Border.regions;
43527 var target = cfg.region;
43530 var r = Roo.bootstrap.layout;
43534 return new r.North(cfg);
43536 return new r.South(cfg);
43538 return new r.East(cfg);
43540 return new r.West(cfg);
43542 return new r.Center(cfg);
43544 throw 'Layout region "'+target+'" not supported.';
43551 * Ext JS Library 1.1.1
43552 * Copyright(c) 2006-2007, Ext JS, LLC.
43554 * Originally Released Under LGPL - original licence link has changed is not relivant.
43557 * <script type="text/javascript">
43561 * @class Roo.bootstrap.layout.Basic
43562 * @extends Roo.util.Observable
43563 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43564 * and does not have a titlebar, tabs or any other features. All it does is size and position
43565 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43566 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43567 * @cfg {string} region the region that it inhabits..
43568 * @cfg {bool} skipConfig skip config?
43572 Roo.bootstrap.layout.Basic = function(config){
43574 this.mgr = config.mgr;
43576 this.position = config.region;
43578 var skipConfig = config.skipConfig;
43582 * @scope Roo.BasicLayoutRegion
43586 * @event beforeremove
43587 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43588 * @param {Roo.LayoutRegion} this
43589 * @param {Roo.ContentPanel} panel The panel
43590 * @param {Object} e The cancel event object
43592 "beforeremove" : true,
43594 * @event invalidated
43595 * Fires when the layout for this region is changed.
43596 * @param {Roo.LayoutRegion} this
43598 "invalidated" : true,
43600 * @event visibilitychange
43601 * Fires when this region is shown or hidden
43602 * @param {Roo.LayoutRegion} this
43603 * @param {Boolean} visibility true or false
43605 "visibilitychange" : true,
43607 * @event paneladded
43608 * Fires when a panel is added.
43609 * @param {Roo.LayoutRegion} this
43610 * @param {Roo.ContentPanel} panel The panel
43612 "paneladded" : true,
43614 * @event panelremoved
43615 * Fires when a panel is removed.
43616 * @param {Roo.LayoutRegion} this
43617 * @param {Roo.ContentPanel} panel The panel
43619 "panelremoved" : true,
43621 * @event beforecollapse
43622 * Fires when this region before collapse.
43623 * @param {Roo.LayoutRegion} this
43625 "beforecollapse" : true,
43628 * Fires when this region is collapsed.
43629 * @param {Roo.LayoutRegion} this
43631 "collapsed" : true,
43634 * Fires when this region is expanded.
43635 * @param {Roo.LayoutRegion} this
43640 * Fires when this region is slid into view.
43641 * @param {Roo.LayoutRegion} this
43643 "slideshow" : true,
43646 * Fires when this region slides out of view.
43647 * @param {Roo.LayoutRegion} this
43649 "slidehide" : true,
43651 * @event panelactivated
43652 * Fires when a panel is activated.
43653 * @param {Roo.LayoutRegion} this
43654 * @param {Roo.ContentPanel} panel The activated panel
43656 "panelactivated" : true,
43659 * Fires when the user resizes this region.
43660 * @param {Roo.LayoutRegion} this
43661 * @param {Number} newSize The new size (width for east/west, height for north/south)
43665 /** A collection of panels in this region. @type Roo.util.MixedCollection */
43666 this.panels = new Roo.util.MixedCollection();
43667 this.panels.getKey = this.getPanelId.createDelegate(this);
43669 this.activePanel = null;
43670 // ensure listeners are added...
43672 if (config.listeners || config.events) {
43673 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43674 listeners : config.listeners || {},
43675 events : config.events || {}
43679 if(skipConfig !== true){
43680 this.applyConfig(config);
43684 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43686 getPanelId : function(p){
43690 applyConfig : function(config){
43691 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43692 this.config = config;
43697 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
43698 * the width, for horizontal (north, south) the height.
43699 * @param {Number} newSize The new width or height
43701 resizeTo : function(newSize){
43702 var el = this.el ? this.el :
43703 (this.activePanel ? this.activePanel.getEl() : null);
43705 switch(this.position){
43708 el.setWidth(newSize);
43709 this.fireEvent("resized", this, newSize);
43713 el.setHeight(newSize);
43714 this.fireEvent("resized", this, newSize);
43720 getBox : function(){
43721 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43724 getMargins : function(){
43725 return this.margins;
43728 updateBox : function(box){
43730 var el = this.activePanel.getEl();
43731 el.dom.style.left = box.x + "px";
43732 el.dom.style.top = box.y + "px";
43733 this.activePanel.setSize(box.width, box.height);
43737 * Returns the container element for this region.
43738 * @return {Roo.Element}
43740 getEl : function(){
43741 return this.activePanel;
43745 * Returns true if this region is currently visible.
43746 * @return {Boolean}
43748 isVisible : function(){
43749 return this.activePanel ? true : false;
43752 setActivePanel : function(panel){
43753 panel = this.getPanel(panel);
43754 if(this.activePanel && this.activePanel != panel){
43755 this.activePanel.setActiveState(false);
43756 this.activePanel.getEl().setLeftTop(-10000,-10000);
43758 this.activePanel = panel;
43759 panel.setActiveState(true);
43761 panel.setSize(this.box.width, this.box.height);
43763 this.fireEvent("panelactivated", this, panel);
43764 this.fireEvent("invalidated");
43768 * Show the specified panel.
43769 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43770 * @return {Roo.ContentPanel} The shown panel or null
43772 showPanel : function(panel){
43773 panel = this.getPanel(panel);
43775 this.setActivePanel(panel);
43781 * Get the active panel for this region.
43782 * @return {Roo.ContentPanel} The active panel or null
43784 getActivePanel : function(){
43785 return this.activePanel;
43789 * Add the passed ContentPanel(s)
43790 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43791 * @return {Roo.ContentPanel} The panel added (if only one was added)
43793 add : function(panel){
43794 if(arguments.length > 1){
43795 for(var i = 0, len = arguments.length; i < len; i++) {
43796 this.add(arguments[i]);
43800 if(this.hasPanel(panel)){
43801 this.showPanel(panel);
43804 var el = panel.getEl();
43805 if(el.dom.parentNode != this.mgr.el.dom){
43806 this.mgr.el.dom.appendChild(el.dom);
43808 if(panel.setRegion){
43809 panel.setRegion(this);
43811 this.panels.add(panel);
43812 el.setStyle("position", "absolute");
43813 if(!panel.background){
43814 this.setActivePanel(panel);
43815 if(this.config.initialSize && this.panels.getCount()==1){
43816 this.resizeTo(this.config.initialSize);
43819 this.fireEvent("paneladded", this, panel);
43824 * Returns true if the panel is in this region.
43825 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43826 * @return {Boolean}
43828 hasPanel : function(panel){
43829 if(typeof panel == "object"){ // must be panel obj
43830 panel = panel.getId();
43832 return this.getPanel(panel) ? true : false;
43836 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43837 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43838 * @param {Boolean} preservePanel Overrides the config preservePanel option
43839 * @return {Roo.ContentPanel} The panel that was removed
43841 remove : function(panel, preservePanel){
43842 panel = this.getPanel(panel);
43847 this.fireEvent("beforeremove", this, panel, e);
43848 if(e.cancel === true){
43851 var panelId = panel.getId();
43852 this.panels.removeKey(panelId);
43857 * Returns the panel specified or null if it's not in this region.
43858 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43859 * @return {Roo.ContentPanel}
43861 getPanel : function(id){
43862 if(typeof id == "object"){ // must be panel obj
43865 return this.panels.get(id);
43869 * Returns this regions position (north/south/east/west/center).
43872 getPosition: function(){
43873 return this.position;
43877 * Ext JS Library 1.1.1
43878 * Copyright(c) 2006-2007, Ext JS, LLC.
43880 * Originally Released Under LGPL - original licence link has changed is not relivant.
43883 * <script type="text/javascript">
43887 * @class Roo.bootstrap.layout.Region
43888 * @extends Roo.bootstrap.layout.Basic
43889 * This class represents a region in a layout manager.
43891 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43892 * @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})
43893 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43894 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43895 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43896 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43897 * @cfg {String} title The title for the region (overrides panel titles)
43898 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43899 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43900 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43901 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43902 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43903 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43904 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43905 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43906 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43907 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43909 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43910 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43911 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43912 * @cfg {Number} width For East/West panels
43913 * @cfg {Number} height For North/South panels
43914 * @cfg {Boolean} split To show the splitter
43915 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43917 * @cfg {string} cls Extra CSS classes to add to region
43919 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43920 * @cfg {string} region the region that it inhabits..
43923 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43924 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43926 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43927 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43928 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43930 Roo.bootstrap.layout.Region = function(config)
43932 this.applyConfig(config);
43934 var mgr = config.mgr;
43935 var pos = config.region;
43936 config.skipConfig = true;
43937 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43940 this.onRender(mgr.el);
43943 this.visible = true;
43944 this.collapsed = false;
43945 this.unrendered_panels = [];
43948 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43950 position: '', // set by wrapper (eg. north/south etc..)
43951 unrendered_panels : null, // unrendered panels.
43953 tabPosition : false,
43955 mgr: false, // points to 'Border'
43958 createBody : function(){
43959 /** This region's body element
43960 * @type Roo.Element */
43961 this.bodyEl = this.el.createChild({
43963 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43967 onRender: function(ctr, pos)
43969 var dh = Roo.DomHelper;
43970 /** This region's container element
43971 * @type Roo.Element */
43972 this.el = dh.append(ctr.dom, {
43974 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43976 /** This region's title element
43977 * @type Roo.Element */
43979 this.titleEl = dh.append(this.el.dom, {
43981 unselectable: "on",
43982 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43984 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43985 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43989 this.titleEl.enableDisplayMode();
43990 /** This region's title text element
43991 * @type HTMLElement */
43992 this.titleTextEl = this.titleEl.dom.firstChild;
43993 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43995 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43996 this.closeBtn.enableDisplayMode();
43997 this.closeBtn.on("click", this.closeClicked, this);
43998 this.closeBtn.hide();
44000 this.createBody(this.config);
44001 if(this.config.hideWhenEmpty){
44003 this.on("paneladded", this.validateVisibility, this);
44004 this.on("panelremoved", this.validateVisibility, this);
44006 if(this.autoScroll){
44007 this.bodyEl.setStyle("overflow", "auto");
44009 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
44011 //if(c.titlebar !== false){
44012 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
44013 this.titleEl.hide();
44015 this.titleEl.show();
44016 if(this.config.title){
44017 this.titleTextEl.innerHTML = this.config.title;
44021 if(this.config.collapsed){
44022 this.collapse(true);
44024 if(this.config.hidden){
44028 if (this.unrendered_panels && this.unrendered_panels.length) {
44029 for (var i =0;i< this.unrendered_panels.length; i++) {
44030 this.add(this.unrendered_panels[i]);
44032 this.unrendered_panels = null;
44038 applyConfig : function(c)
44041 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
44042 var dh = Roo.DomHelper;
44043 if(c.titlebar !== false){
44044 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
44045 this.collapseBtn.on("click", this.collapse, this);
44046 this.collapseBtn.enableDisplayMode();
44048 if(c.showPin === true || this.showPin){
44049 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
44050 this.stickBtn.enableDisplayMode();
44051 this.stickBtn.on("click", this.expand, this);
44052 this.stickBtn.hide();
44057 /** This region's collapsed element
44058 * @type Roo.Element */
44061 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44062 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44065 if(c.floatable !== false){
44066 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44067 this.collapsedEl.on("click", this.collapseClick, this);
44070 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44071 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44072 id: "message", unselectable: "on", style:{"float":"left"}});
44073 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44075 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44076 this.expandBtn.on("click", this.expand, this);
44080 if(this.collapseBtn){
44081 this.collapseBtn.setVisible(c.collapsible == true);
44084 this.cmargins = c.cmargins || this.cmargins ||
44085 (this.position == "west" || this.position == "east" ?
44086 {top: 0, left: 2, right:2, bottom: 0} :
44087 {top: 2, left: 0, right:0, bottom: 2});
44089 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44092 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
44094 this.autoScroll = c.autoScroll || false;
44099 this.duration = c.duration || .30;
44100 this.slideDuration = c.slideDuration || .45;
44105 * Returns true if this region is currently visible.
44106 * @return {Boolean}
44108 isVisible : function(){
44109 return this.visible;
44113 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44114 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
44116 //setCollapsedTitle : function(title){
44117 // title = title || " ";
44118 // if(this.collapsedTitleTextEl){
44119 // this.collapsedTitleTextEl.innerHTML = title;
44123 getBox : function(){
44125 // if(!this.collapsed){
44126 b = this.el.getBox(false, true);
44128 // b = this.collapsedEl.getBox(false, true);
44133 getMargins : function(){
44134 return this.margins;
44135 //return this.collapsed ? this.cmargins : this.margins;
44138 highlight : function(){
44139 this.el.addClass("x-layout-panel-dragover");
44142 unhighlight : function(){
44143 this.el.removeClass("x-layout-panel-dragover");
44146 updateBox : function(box)
44148 if (!this.bodyEl) {
44149 return; // not rendered yet..
44153 if(!this.collapsed){
44154 this.el.dom.style.left = box.x + "px";
44155 this.el.dom.style.top = box.y + "px";
44156 this.updateBody(box.width, box.height);
44158 this.collapsedEl.dom.style.left = box.x + "px";
44159 this.collapsedEl.dom.style.top = box.y + "px";
44160 this.collapsedEl.setSize(box.width, box.height);
44163 this.tabs.autoSizeTabs();
44167 updateBody : function(w, h)
44170 this.el.setWidth(w);
44171 w -= this.el.getBorderWidth("rl");
44172 if(this.config.adjustments){
44173 w += this.config.adjustments[0];
44176 if(h !== null && h > 0){
44177 this.el.setHeight(h);
44178 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44179 h -= this.el.getBorderWidth("tb");
44180 if(this.config.adjustments){
44181 h += this.config.adjustments[1];
44183 this.bodyEl.setHeight(h);
44185 h = this.tabs.syncHeight(h);
44188 if(this.panelSize){
44189 w = w !== null ? w : this.panelSize.width;
44190 h = h !== null ? h : this.panelSize.height;
44192 if(this.activePanel){
44193 var el = this.activePanel.getEl();
44194 w = w !== null ? w : el.getWidth();
44195 h = h !== null ? h : el.getHeight();
44196 this.panelSize = {width: w, height: h};
44197 this.activePanel.setSize(w, h);
44199 if(Roo.isIE && this.tabs){
44200 this.tabs.el.repaint();
44205 * Returns the container element for this region.
44206 * @return {Roo.Element}
44208 getEl : function(){
44213 * Hides this region.
44216 //if(!this.collapsed){
44217 this.el.dom.style.left = "-2000px";
44220 // this.collapsedEl.dom.style.left = "-2000px";
44221 // this.collapsedEl.hide();
44223 this.visible = false;
44224 this.fireEvent("visibilitychange", this, false);
44228 * Shows this region if it was previously hidden.
44231 //if(!this.collapsed){
44234 // this.collapsedEl.show();
44236 this.visible = true;
44237 this.fireEvent("visibilitychange", this, true);
44240 closeClicked : function(){
44241 if(this.activePanel){
44242 this.remove(this.activePanel);
44246 collapseClick : function(e){
44248 e.stopPropagation();
44251 e.stopPropagation();
44257 * Collapses this region.
44258 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44261 collapse : function(skipAnim, skipCheck = false){
44262 if(this.collapsed) {
44266 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
44268 this.collapsed = true;
44270 this.split.el.hide();
44272 if(this.config.animate && skipAnim !== true){
44273 this.fireEvent("invalidated", this);
44274 this.animateCollapse();
44276 this.el.setLocation(-20000,-20000);
44278 this.collapsedEl.show();
44279 this.fireEvent("collapsed", this);
44280 this.fireEvent("invalidated", this);
44286 animateCollapse : function(){
44291 * Expands this region if it was previously collapsed.
44292 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44293 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44296 expand : function(e, skipAnim){
44298 e.stopPropagation();
44300 if(!this.collapsed || this.el.hasActiveFx()) {
44304 this.afterSlideIn();
44307 this.collapsed = false;
44308 if(this.config.animate && skipAnim !== true){
44309 this.animateExpand();
44313 this.split.el.show();
44315 this.collapsedEl.setLocation(-2000,-2000);
44316 this.collapsedEl.hide();
44317 this.fireEvent("invalidated", this);
44318 this.fireEvent("expanded", this);
44322 animateExpand : function(){
44326 initTabs : function()
44328 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44330 var ts = new Roo.bootstrap.panel.Tabs({
44331 el: this.bodyEl.dom,
44333 tabPosition: this.tabPosition ? this.tabPosition : 'top',
44334 disableTooltips: this.config.disableTabTips,
44335 toolbar : this.config.toolbar
44338 if(this.config.hideTabs){
44339 ts.stripWrap.setDisplayed(false);
44342 ts.resizeTabs = this.config.resizeTabs === true;
44343 ts.minTabWidth = this.config.minTabWidth || 40;
44344 ts.maxTabWidth = this.config.maxTabWidth || 250;
44345 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44346 ts.monitorResize = false;
44347 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44348 ts.bodyEl.addClass('roo-layout-tabs-body');
44349 this.panels.each(this.initPanelAsTab, this);
44352 initPanelAsTab : function(panel){
44353 var ti = this.tabs.addTab(
44357 this.config.closeOnTab && panel.isClosable(),
44360 if(panel.tabTip !== undefined){
44361 ti.setTooltip(panel.tabTip);
44363 ti.on("activate", function(){
44364 this.setActivePanel(panel);
44367 if(this.config.closeOnTab){
44368 ti.on("beforeclose", function(t, e){
44370 this.remove(panel);
44374 panel.tabItem = ti;
44379 updatePanelTitle : function(panel, title)
44381 if(this.activePanel == panel){
44382 this.updateTitle(title);
44385 var ti = this.tabs.getTab(panel.getEl().id);
44387 if(panel.tabTip !== undefined){
44388 ti.setTooltip(panel.tabTip);
44393 updateTitle : function(title){
44394 if(this.titleTextEl && !this.config.title){
44395 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
44399 setActivePanel : function(panel)
44401 panel = this.getPanel(panel);
44402 if(this.activePanel && this.activePanel != panel){
44403 if(this.activePanel.setActiveState(false) === false){
44407 this.activePanel = panel;
44408 panel.setActiveState(true);
44409 if(this.panelSize){
44410 panel.setSize(this.panelSize.width, this.panelSize.height);
44413 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44415 this.updateTitle(panel.getTitle());
44417 this.fireEvent("invalidated", this);
44419 this.fireEvent("panelactivated", this, panel);
44423 * Shows the specified panel.
44424 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44425 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44427 showPanel : function(panel)
44429 panel = this.getPanel(panel);
44432 var tab = this.tabs.getTab(panel.getEl().id);
44433 if(tab.isHidden()){
44434 this.tabs.unhideTab(tab.id);
44438 this.setActivePanel(panel);
44445 * Get the active panel for this region.
44446 * @return {Roo.ContentPanel} The active panel or null
44448 getActivePanel : function(){
44449 return this.activePanel;
44452 validateVisibility : function(){
44453 if(this.panels.getCount() < 1){
44454 this.updateTitle(" ");
44455 this.closeBtn.hide();
44458 if(!this.isVisible()){
44465 * Adds the passed ContentPanel(s) to this region.
44466 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44467 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44469 add : function(panel)
44471 if(arguments.length > 1){
44472 for(var i = 0, len = arguments.length; i < len; i++) {
44473 this.add(arguments[i]);
44478 // if we have not been rendered yet, then we can not really do much of this..
44479 if (!this.bodyEl) {
44480 this.unrendered_panels.push(panel);
44487 if(this.hasPanel(panel)){
44488 this.showPanel(panel);
44491 panel.setRegion(this);
44492 this.panels.add(panel);
44493 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44494 // sinle panel - no tab...?? would it not be better to render it with the tabs,
44495 // and hide them... ???
44496 this.bodyEl.dom.appendChild(panel.getEl().dom);
44497 if(panel.background !== true){
44498 this.setActivePanel(panel);
44500 this.fireEvent("paneladded", this, panel);
44507 this.initPanelAsTab(panel);
44511 if(panel.background !== true){
44512 this.tabs.activate(panel.getEl().id);
44514 this.fireEvent("paneladded", this, panel);
44519 * Hides the tab for the specified panel.
44520 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44522 hidePanel : function(panel){
44523 if(this.tabs && (panel = this.getPanel(panel))){
44524 this.tabs.hideTab(panel.getEl().id);
44529 * Unhides the tab for a previously hidden panel.
44530 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44532 unhidePanel : function(panel){
44533 if(this.tabs && (panel = this.getPanel(panel))){
44534 this.tabs.unhideTab(panel.getEl().id);
44538 clearPanels : function(){
44539 while(this.panels.getCount() > 0){
44540 this.remove(this.panels.first());
44545 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44546 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44547 * @param {Boolean} preservePanel Overrides the config preservePanel option
44548 * @return {Roo.ContentPanel} The panel that was removed
44550 remove : function(panel, preservePanel)
44552 panel = this.getPanel(panel);
44557 this.fireEvent("beforeremove", this, panel, e);
44558 if(e.cancel === true){
44561 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44562 var panelId = panel.getId();
44563 this.panels.removeKey(panelId);
44565 document.body.appendChild(panel.getEl().dom);
44568 this.tabs.removeTab(panel.getEl().id);
44569 }else if (!preservePanel){
44570 this.bodyEl.dom.removeChild(panel.getEl().dom);
44572 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44573 var p = this.panels.first();
44574 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44575 tempEl.appendChild(p.getEl().dom);
44576 this.bodyEl.update("");
44577 this.bodyEl.dom.appendChild(p.getEl().dom);
44579 this.updateTitle(p.getTitle());
44581 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44582 this.setActivePanel(p);
44584 panel.setRegion(null);
44585 if(this.activePanel == panel){
44586 this.activePanel = null;
44588 if(this.config.autoDestroy !== false && preservePanel !== true){
44589 try{panel.destroy();}catch(e){}
44591 this.fireEvent("panelremoved", this, panel);
44596 * Returns the TabPanel component used by this region
44597 * @return {Roo.TabPanel}
44599 getTabs : function(){
44603 createTool : function(parentEl, className){
44604 var btn = Roo.DomHelper.append(parentEl, {
44606 cls: "x-layout-tools-button",
44609 cls: "roo-layout-tools-button-inner " + className,
44613 btn.addClassOnOver("roo-layout-tools-button-over");
44618 * Ext JS Library 1.1.1
44619 * Copyright(c) 2006-2007, Ext JS, LLC.
44621 * Originally Released Under LGPL - original licence link has changed is not relivant.
44624 * <script type="text/javascript">
44630 * @class Roo.SplitLayoutRegion
44631 * @extends Roo.LayoutRegion
44632 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44634 Roo.bootstrap.layout.Split = function(config){
44635 this.cursor = config.cursor;
44636 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44639 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44641 splitTip : "Drag to resize.",
44642 collapsibleSplitTip : "Drag to resize. Double click to hide.",
44643 useSplitTips : false,
44645 applyConfig : function(config){
44646 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44649 onRender : function(ctr,pos) {
44651 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44652 if(!this.config.split){
44657 var splitEl = Roo.DomHelper.append(ctr.dom, {
44659 id: this.el.id + "-split",
44660 cls: "roo-layout-split roo-layout-split-"+this.position,
44663 /** The SplitBar for this region
44664 * @type Roo.SplitBar */
44665 // does not exist yet...
44666 Roo.log([this.position, this.orientation]);
44668 this.split = new Roo.bootstrap.SplitBar({
44669 dragElement : splitEl,
44670 resizingElement: this.el,
44671 orientation : this.orientation
44674 this.split.on("moved", this.onSplitMove, this);
44675 this.split.useShim = this.config.useShim === true;
44676 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44677 if(this.useSplitTips){
44678 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44680 //if(config.collapsible){
44681 // this.split.el.on("dblclick", this.collapse, this);
44684 if(typeof this.config.minSize != "undefined"){
44685 this.split.minSize = this.config.minSize;
44687 if(typeof this.config.maxSize != "undefined"){
44688 this.split.maxSize = this.config.maxSize;
44690 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44691 this.hideSplitter();
44696 getHMaxSize : function(){
44697 var cmax = this.config.maxSize || 10000;
44698 var center = this.mgr.getRegion("center");
44699 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44702 getVMaxSize : function(){
44703 var cmax = this.config.maxSize || 10000;
44704 var center = this.mgr.getRegion("center");
44705 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44708 onSplitMove : function(split, newSize){
44709 this.fireEvent("resized", this, newSize);
44713 * Returns the {@link Roo.SplitBar} for this region.
44714 * @return {Roo.SplitBar}
44716 getSplitBar : function(){
44721 this.hideSplitter();
44722 Roo.bootstrap.layout.Split.superclass.hide.call(this);
44725 hideSplitter : function(){
44727 this.split.el.setLocation(-2000,-2000);
44728 this.split.el.hide();
44734 this.split.el.show();
44736 Roo.bootstrap.layout.Split.superclass.show.call(this);
44739 beforeSlide: function(){
44740 if(Roo.isGecko){// firefox overflow auto bug workaround
44741 this.bodyEl.clip();
44743 this.tabs.bodyEl.clip();
44745 if(this.activePanel){
44746 this.activePanel.getEl().clip();
44748 if(this.activePanel.beforeSlide){
44749 this.activePanel.beforeSlide();
44755 afterSlide : function(){
44756 if(Roo.isGecko){// firefox overflow auto bug workaround
44757 this.bodyEl.unclip();
44759 this.tabs.bodyEl.unclip();
44761 if(this.activePanel){
44762 this.activePanel.getEl().unclip();
44763 if(this.activePanel.afterSlide){
44764 this.activePanel.afterSlide();
44770 initAutoHide : function(){
44771 if(this.autoHide !== false){
44772 if(!this.autoHideHd){
44773 var st = new Roo.util.DelayedTask(this.slideIn, this);
44774 this.autoHideHd = {
44775 "mouseout": function(e){
44776 if(!e.within(this.el, true)){
44780 "mouseover" : function(e){
44786 this.el.on(this.autoHideHd);
44790 clearAutoHide : function(){
44791 if(this.autoHide !== false){
44792 this.el.un("mouseout", this.autoHideHd.mouseout);
44793 this.el.un("mouseover", this.autoHideHd.mouseover);
44797 clearMonitor : function(){
44798 Roo.get(document).un("click", this.slideInIf, this);
44801 // these names are backwards but not changed for compat
44802 slideOut : function(){
44803 if(this.isSlid || this.el.hasActiveFx()){
44806 this.isSlid = true;
44807 if(this.collapseBtn){
44808 this.collapseBtn.hide();
44810 this.closeBtnState = this.closeBtn.getStyle('display');
44811 this.closeBtn.hide();
44813 this.stickBtn.show();
44816 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44817 this.beforeSlide();
44818 this.el.setStyle("z-index", 10001);
44819 this.el.slideIn(this.getSlideAnchor(), {
44820 callback: function(){
44822 this.initAutoHide();
44823 Roo.get(document).on("click", this.slideInIf, this);
44824 this.fireEvent("slideshow", this);
44831 afterSlideIn : function(){
44832 this.clearAutoHide();
44833 this.isSlid = false;
44834 this.clearMonitor();
44835 this.el.setStyle("z-index", "");
44836 if(this.collapseBtn){
44837 this.collapseBtn.show();
44839 this.closeBtn.setStyle('display', this.closeBtnState);
44841 this.stickBtn.hide();
44843 this.fireEvent("slidehide", this);
44846 slideIn : function(cb){
44847 if(!this.isSlid || this.el.hasActiveFx()){
44851 this.isSlid = false;
44852 this.beforeSlide();
44853 this.el.slideOut(this.getSlideAnchor(), {
44854 callback: function(){
44855 this.el.setLeftTop(-10000, -10000);
44857 this.afterSlideIn();
44865 slideInIf : function(e){
44866 if(!e.within(this.el)){
44871 animateCollapse : function(){
44872 this.beforeSlide();
44873 this.el.setStyle("z-index", 20000);
44874 var anchor = this.getSlideAnchor();
44875 this.el.slideOut(anchor, {
44876 callback : function(){
44877 this.el.setStyle("z-index", "");
44878 this.collapsedEl.slideIn(anchor, {duration:.3});
44880 this.el.setLocation(-10000,-10000);
44882 this.fireEvent("collapsed", this);
44889 animateExpand : function(){
44890 this.beforeSlide();
44891 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44892 this.el.setStyle("z-index", 20000);
44893 this.collapsedEl.hide({
44896 this.el.slideIn(this.getSlideAnchor(), {
44897 callback : function(){
44898 this.el.setStyle("z-index", "");
44901 this.split.el.show();
44903 this.fireEvent("invalidated", this);
44904 this.fireEvent("expanded", this);
44932 getAnchor : function(){
44933 return this.anchors[this.position];
44936 getCollapseAnchor : function(){
44937 return this.canchors[this.position];
44940 getSlideAnchor : function(){
44941 return this.sanchors[this.position];
44944 getAlignAdj : function(){
44945 var cm = this.cmargins;
44946 switch(this.position){
44962 getExpandAdj : function(){
44963 var c = this.collapsedEl, cm = this.cmargins;
44964 switch(this.position){
44966 return [-(cm.right+c.getWidth()+cm.left), 0];
44969 return [cm.right+c.getWidth()+cm.left, 0];
44972 return [0, -(cm.top+cm.bottom+c.getHeight())];
44975 return [0, cm.top+cm.bottom+c.getHeight()];
44981 * Ext JS Library 1.1.1
44982 * Copyright(c) 2006-2007, Ext JS, LLC.
44984 * Originally Released Under LGPL - original licence link has changed is not relivant.
44987 * <script type="text/javascript">
44990 * These classes are private internal classes
44992 Roo.bootstrap.layout.Center = function(config){
44993 config.region = "center";
44994 Roo.bootstrap.layout.Region.call(this, config);
44995 this.visible = true;
44996 this.minWidth = config.minWidth || 20;
44997 this.minHeight = config.minHeight || 20;
45000 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
45002 // center panel can't be hidden
45006 // center panel can't be hidden
45009 getMinWidth: function(){
45010 return this.minWidth;
45013 getMinHeight: function(){
45014 return this.minHeight;
45028 Roo.bootstrap.layout.North = function(config)
45030 config.region = 'north';
45031 config.cursor = 'n-resize';
45033 Roo.bootstrap.layout.Split.call(this, config);
45037 this.split.placement = Roo.bootstrap.SplitBar.TOP;
45038 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45039 this.split.el.addClass("roo-layout-split-v");
45041 //var size = config.initialSize || config.height;
45042 //if(this.el && typeof size != "undefined"){
45043 // this.el.setHeight(size);
45046 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
45048 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45051 onRender : function(ctr, pos)
45053 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45054 var size = this.config.initialSize || this.config.height;
45055 if(this.el && typeof size != "undefined"){
45056 this.el.setHeight(size);
45061 getBox : function(){
45062 if(this.collapsed){
45063 return this.collapsedEl.getBox();
45065 var box = this.el.getBox();
45067 box.height += this.split.el.getHeight();
45072 updateBox : function(box){
45073 if(this.split && !this.collapsed){
45074 box.height -= this.split.el.getHeight();
45075 this.split.el.setLeft(box.x);
45076 this.split.el.setTop(box.y+box.height);
45077 this.split.el.setWidth(box.width);
45079 if(this.collapsed){
45080 this.updateBody(box.width, null);
45082 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45090 Roo.bootstrap.layout.South = function(config){
45091 config.region = 'south';
45092 config.cursor = 's-resize';
45093 Roo.bootstrap.layout.Split.call(this, config);
45095 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
45096 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
45097 this.split.el.addClass("roo-layout-split-v");
45102 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
45103 orientation: Roo.bootstrap.SplitBar.VERTICAL,
45105 onRender : function(ctr, pos)
45107 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45108 var size = this.config.initialSize || this.config.height;
45109 if(this.el && typeof size != "undefined"){
45110 this.el.setHeight(size);
45115 getBox : function(){
45116 if(this.collapsed){
45117 return this.collapsedEl.getBox();
45119 var box = this.el.getBox();
45121 var sh = this.split.el.getHeight();
45128 updateBox : function(box){
45129 if(this.split && !this.collapsed){
45130 var sh = this.split.el.getHeight();
45133 this.split.el.setLeft(box.x);
45134 this.split.el.setTop(box.y-sh);
45135 this.split.el.setWidth(box.width);
45137 if(this.collapsed){
45138 this.updateBody(box.width, null);
45140 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45144 Roo.bootstrap.layout.East = function(config){
45145 config.region = "east";
45146 config.cursor = "e-resize";
45147 Roo.bootstrap.layout.Split.call(this, config);
45149 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
45150 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45151 this.split.el.addClass("roo-layout-split-h");
45155 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
45156 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45158 onRender : function(ctr, pos)
45160 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
45161 var size = this.config.initialSize || this.config.width;
45162 if(this.el && typeof size != "undefined"){
45163 this.el.setWidth(size);
45168 getBox : function(){
45169 if(this.collapsed){
45170 return this.collapsedEl.getBox();
45172 var box = this.el.getBox();
45174 var sw = this.split.el.getWidth();
45181 updateBox : function(box){
45182 if(this.split && !this.collapsed){
45183 var sw = this.split.el.getWidth();
45185 this.split.el.setLeft(box.x);
45186 this.split.el.setTop(box.y);
45187 this.split.el.setHeight(box.height);
45190 if(this.collapsed){
45191 this.updateBody(null, box.height);
45193 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45197 Roo.bootstrap.layout.West = function(config){
45198 config.region = "west";
45199 config.cursor = "w-resize";
45201 Roo.bootstrap.layout.Split.call(this, config);
45203 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
45204 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
45205 this.split.el.addClass("roo-layout-split-h");
45209 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
45210 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
45212 onRender: function(ctr, pos)
45214 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
45215 var size = this.config.initialSize || this.config.width;
45216 if(typeof size != "undefined"){
45217 this.el.setWidth(size);
45221 getBox : function(){
45222 if(this.collapsed){
45223 return this.collapsedEl.getBox();
45225 var box = this.el.getBox();
45226 if (box.width == 0) {
45227 box.width = this.config.width; // kludge?
45230 box.width += this.split.el.getWidth();
45235 updateBox : function(box){
45236 if(this.split && !this.collapsed){
45237 var sw = this.split.el.getWidth();
45239 this.split.el.setLeft(box.x+box.width);
45240 this.split.el.setTop(box.y);
45241 this.split.el.setHeight(box.height);
45243 if(this.collapsed){
45244 this.updateBody(null, box.height);
45246 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
45250 * Ext JS Library 1.1.1
45251 * Copyright(c) 2006-2007, Ext JS, LLC.
45253 * Originally Released Under LGPL - original licence link has changed is not relivant.
45256 * <script type="text/javascript">
45259 * @class Roo.bootstrap.paenl.Content
45260 * @extends Roo.util.Observable
45261 * @children Roo.bootstrap.Component
45262 * @parent builder Roo.bootstrap.layout.Border
45263 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
45264 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
45265 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
45266 * @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
45267 * @cfg {Boolean} closable True if the panel can be closed/removed
45268 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45269 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45270 * @cfg {Toolbar} toolbar A toolbar for this panel
45271 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45272 * @cfg {String} title The title for this panel
45273 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45274 * @cfg {String} url Calls {@link #setUrl} with this value
45275 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45276 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45277 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45278 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
45279 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
45280 * @cfg {Boolean} badges render the badges
45281 * @cfg {String} cls extra classes to use
45282 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
45285 * Create a new ContentPanel.
45286 * @param {String/Object} config A string to set only the title or a config object
45289 Roo.bootstrap.panel.Content = function( config){
45291 this.tpl = config.tpl || false;
45293 var el = config.el;
45294 var content = config.content;
45296 if(config.autoCreate){ // xtype is available if this is called from factory
45299 this.el = Roo.get(el);
45300 if(!this.el && config && config.autoCreate){
45301 if(typeof config.autoCreate == "object"){
45302 if(!config.autoCreate.id){
45303 config.autoCreate.id = config.id||el;
45305 this.el = Roo.DomHelper.append(document.body,
45306 config.autoCreate, true);
45310 cls: (config.cls || '') +
45311 (config.background ? ' bg-' + config.background : '') +
45312 " roo-layout-inactive-content",
45315 if (config.iframe) {
45319 style : 'border: 0px',
45320 src : 'about:blank'
45326 elcfg.html = config.html;
45330 this.el = Roo.DomHelper.append(document.body, elcfg , true);
45331 if (config.iframe) {
45332 this.iframeEl = this.el.select('iframe',true).first();
45337 this.closable = false;
45338 this.loaded = false;
45339 this.active = false;
45342 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45344 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45346 this.wrapEl = this.el; //this.el.wrap();
45348 if (config.toolbar.items) {
45349 ti = config.toolbar.items ;
45350 delete config.toolbar.items ;
45354 this.toolbar.render(this.wrapEl, 'before');
45355 for(var i =0;i < ti.length;i++) {
45356 // Roo.log(['add child', items[i]]);
45357 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45359 this.toolbar.items = nitems;
45360 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45361 delete config.toolbar;
45365 // xtype created footer. - not sure if will work as we normally have to render first..
45366 if (this.footer && !this.footer.el && this.footer.xtype) {
45367 if (!this.wrapEl) {
45368 this.wrapEl = this.el.wrap();
45371 this.footer.container = this.wrapEl.createChild();
45373 this.footer = Roo.factory(this.footer, Roo);
45378 if(typeof config == "string"){
45379 this.title = config;
45381 Roo.apply(this, config);
45385 this.resizeEl = Roo.get(this.resizeEl, true);
45387 this.resizeEl = this.el;
45389 // handle view.xtype
45397 * Fires when this panel is activated.
45398 * @param {Roo.ContentPanel} this
45402 * @event deactivate
45403 * Fires when this panel is activated.
45404 * @param {Roo.ContentPanel} this
45406 "deactivate" : true,
45410 * Fires when this panel is resized if fitToFrame is true.
45411 * @param {Roo.ContentPanel} this
45412 * @param {Number} width The width after any component adjustments
45413 * @param {Number} height The height after any component adjustments
45419 * Fires when this tab is created
45420 * @param {Roo.ContentPanel} this
45426 * Fires when this content is scrolled
45427 * @param {Roo.ContentPanel} this
45428 * @param {Event} scrollEvent
45439 if(this.autoScroll && !this.iframe){
45440 this.resizeEl.setStyle("overflow", "auto");
45441 this.resizeEl.on('scroll', this.onScroll, this);
45443 // fix randome scrolling
45444 //this.el.on('scroll', function() {
45445 // Roo.log('fix random scolling');
45446 // this.scrollTo('top',0);
45449 content = content || this.content;
45451 this.setContent(content);
45453 if(config && config.url){
45454 this.setUrl(this.url, this.params, this.loadOnce);
45459 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45461 if (this.view && typeof(this.view.xtype) != 'undefined') {
45462 this.view.el = this.el.appendChild(document.createElement("div"));
45463 this.view = Roo.factory(this.view);
45464 this.view.render && this.view.render(false, '');
45468 this.fireEvent('render', this);
45471 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45481 /* Resize Element - use this to work out scroll etc. */
45484 setRegion : function(region){
45485 this.region = region;
45486 this.setActiveClass(region && !this.background);
45490 setActiveClass: function(state)
45493 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45494 this.el.setStyle('position','relative');
45496 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45497 this.el.setStyle('position', 'absolute');
45502 * Returns the toolbar for this Panel if one was configured.
45503 * @return {Roo.Toolbar}
45505 getToolbar : function(){
45506 return this.toolbar;
45509 setActiveState : function(active)
45511 this.active = active;
45512 this.setActiveClass(active);
45514 if(this.fireEvent("deactivate", this) === false){
45519 this.fireEvent("activate", this);
45523 * Updates this panel's element (not for iframe)
45524 * @param {String} content The new content
45525 * @param {Boolean} loadScripts (optional) true to look for and process scripts
45527 setContent : function(content, loadScripts){
45532 this.el.update(content, loadScripts);
45535 ignoreResize : function(w, h)
45537 //return false; // always resize?
45538 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45541 this.lastSize = {width: w, height: h};
45546 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45547 * @return {Roo.UpdateManager} The UpdateManager
45549 getUpdateManager : function(){
45553 return this.el.getUpdateManager();
45556 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45557 * Does not work with IFRAME contents
45558 * @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:
45561 url: "your-url.php",
45562 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45563 callback: yourFunction,
45564 scope: yourObject, //(optional scope)
45567 text: "Loading...",
45573 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45574 * 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.
45575 * @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}
45576 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45577 * @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.
45578 * @return {Roo.ContentPanel} this
45586 var um = this.el.getUpdateManager();
45587 um.update.apply(um, arguments);
45593 * 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.
45594 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45595 * @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)
45596 * @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)
45597 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45599 setUrl : function(url, params, loadOnce){
45601 this.iframeEl.dom.src = url;
45605 if(this.refreshDelegate){
45606 this.removeListener("activate", this.refreshDelegate);
45608 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45609 this.on("activate", this.refreshDelegate);
45610 return this.el.getUpdateManager();
45613 _handleRefresh : function(url, params, loadOnce){
45614 if(!loadOnce || !this.loaded){
45615 var updater = this.el.getUpdateManager();
45616 updater.update(url, params, this._setLoaded.createDelegate(this));
45620 _setLoaded : function(){
45621 this.loaded = true;
45625 * Returns this panel's id
45628 getId : function(){
45633 * Returns this panel's element - used by regiosn to add.
45634 * @return {Roo.Element}
45636 getEl : function(){
45637 return this.wrapEl || this.el;
45642 adjustForComponents : function(width, height)
45644 //Roo.log('adjustForComponents ');
45645 if(this.resizeEl != this.el){
45646 width -= this.el.getFrameWidth('lr');
45647 height -= this.el.getFrameWidth('tb');
45650 var te = this.toolbar.getEl();
45651 te.setWidth(width);
45652 height -= te.getHeight();
45655 var te = this.footer.getEl();
45656 te.setWidth(width);
45657 height -= te.getHeight();
45661 if(this.adjustments){
45662 width += this.adjustments[0];
45663 height += this.adjustments[1];
45665 return {"width": width, "height": height};
45668 setSize : function(width, height){
45669 if(this.fitToFrame && !this.ignoreResize(width, height)){
45670 if(this.fitContainer && this.resizeEl != this.el){
45671 this.el.setSize(width, height);
45673 var size = this.adjustForComponents(width, height);
45675 this.iframeEl.setSize(width,height);
45678 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45679 this.fireEvent('resize', this, size.width, size.height);
45686 * Returns this panel's title
45689 getTitle : function(){
45691 if (typeof(this.title) != 'object') {
45696 for (var k in this.title) {
45697 if (!this.title.hasOwnProperty(k)) {
45701 if (k.indexOf('-') >= 0) {
45702 var s = k.split('-');
45703 for (var i = 0; i<s.length; i++) {
45704 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45707 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45714 * Set this panel's title
45715 * @param {String} title
45717 setTitle : function(title){
45718 this.title = title;
45720 this.region.updatePanelTitle(this, title);
45725 * Returns true is this panel was configured to be closable
45726 * @return {Boolean}
45728 isClosable : function(){
45729 return this.closable;
45732 beforeSlide : function(){
45734 this.resizeEl.clip();
45737 afterSlide : function(){
45739 this.resizeEl.unclip();
45743 * Force a content refresh from the URL specified in the {@link #setUrl} method.
45744 * Will fail silently if the {@link #setUrl} method has not been called.
45745 * This does not activate the panel, just updates its content.
45747 refresh : function(){
45748 if(this.refreshDelegate){
45749 this.loaded = false;
45750 this.refreshDelegate();
45755 * Destroys this panel
45757 destroy : function(){
45758 this.el.removeAllListeners();
45759 var tempEl = document.createElement("span");
45760 tempEl.appendChild(this.el.dom);
45761 tempEl.innerHTML = "";
45767 * form - if the content panel contains a form - this is a reference to it.
45768 * @type {Roo.form.Form}
45772 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45773 * This contains a reference to it.
45779 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45789 * @param {Object} cfg Xtype definition of item to add.
45793 getChildContainer: function () {
45794 return this.getEl();
45798 onScroll : function(e)
45800 this.fireEvent('scroll', this, e);
45805 var ret = new Roo.factory(cfg);
45810 if (cfg.xtype.match(/^Form$/)) {
45813 //if (this.footer) {
45814 // el = this.footer.container.insertSibling(false, 'before');
45816 el = this.el.createChild();
45819 this.form = new Roo.form.Form(cfg);
45822 if ( this.form.allItems.length) {
45823 this.form.render(el.dom);
45827 // should only have one of theses..
45828 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45829 // views.. should not be just added - used named prop 'view''
45831 cfg.el = this.el.appendChild(document.createElement("div"));
45834 var ret = new Roo.factory(cfg);
45836 ret.render && ret.render(false, ''); // render blank..
45846 * @class Roo.bootstrap.panel.Grid
45847 * @extends Roo.bootstrap.panel.Content
45849 * Create a new GridPanel.
45850 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45851 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45852 * @param {Object} config A the config object
45858 Roo.bootstrap.panel.Grid = function(config)
45862 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45863 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45865 config.el = this.wrapper;
45866 //this.el = this.wrapper;
45868 if (config.container) {
45869 // ctor'ed from a Border/panel.grid
45872 this.wrapper.setStyle("overflow", "hidden");
45873 this.wrapper.addClass('roo-grid-container');
45878 if(config.toolbar){
45879 var tool_el = this.wrapper.createChild();
45880 this.toolbar = Roo.factory(config.toolbar);
45882 if (config.toolbar.items) {
45883 ti = config.toolbar.items ;
45884 delete config.toolbar.items ;
45888 this.toolbar.render(tool_el);
45889 for(var i =0;i < ti.length;i++) {
45890 // Roo.log(['add child', items[i]]);
45891 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45893 this.toolbar.items = nitems;
45895 delete config.toolbar;
45898 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45899 config.grid.scrollBody = true;;
45900 config.grid.monitorWindowResize = false; // turn off autosizing
45901 config.grid.autoHeight = false;
45902 config.grid.autoWidth = false;
45904 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45906 if (config.background) {
45907 // render grid on panel activation (if panel background)
45908 this.on('activate', function(gp) {
45909 if (!gp.grid.rendered) {
45910 gp.grid.render(this.wrapper);
45911 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45916 this.grid.render(this.wrapper);
45917 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45920 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45921 // ??? needed ??? config.el = this.wrapper;
45926 // xtype created footer. - not sure if will work as we normally have to render first..
45927 if (this.footer && !this.footer.el && this.footer.xtype) {
45929 var ctr = this.grid.getView().getFooterPanel(true);
45930 this.footer.dataSource = this.grid.dataSource;
45931 this.footer = Roo.factory(this.footer, Roo);
45932 this.footer.render(ctr);
45942 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45945 getId : function(){
45946 return this.grid.id;
45950 * Returns the grid for this panel
45951 * @return {Roo.bootstrap.Table}
45953 getGrid : function(){
45957 setSize : function(width, height)
45960 //if(!this.ignoreResize(width, height)){
45961 var grid = this.grid;
45962 var size = this.adjustForComponents(width, height);
45963 // tfoot is not a footer?
45966 var gridel = grid.getGridEl();
45967 gridel.setSize(size.width, size.height);
45969 var tbd = grid.getGridEl().select('tbody', true).first();
45970 var thd = grid.getGridEl().select('thead',true).first();
45971 var tbf= grid.getGridEl().select('tfoot', true).first();
45974 size.height -= tbf.getHeight();
45977 size.height -= thd.getHeight();
45980 tbd.setSize(size.width, size.height );
45981 // this is for the account management tab -seems to work there.
45982 var thd = grid.getGridEl().select('thead',true).first();
45984 // tbd.setSize(size.width, size.height - thd.getHeight());
45994 beforeSlide : function(){
45995 this.grid.getView().scroller.clip();
45998 afterSlide : function(){
45999 this.grid.getView().scroller.unclip();
46002 destroy : function(){
46003 this.grid.destroy();
46005 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
46010 * @class Roo.bootstrap.panel.Nest
46011 * @extends Roo.bootstrap.panel.Content
46013 * Create a new Panel, that can contain a layout.Border.
46016 * @param {String/Object} config A string to set only the title or a config object
46018 Roo.bootstrap.panel.Nest = function(config)
46020 // construct with only one argument..
46021 /* FIXME - implement nicer consturctors
46022 if (layout.layout) {
46024 layout = config.layout;
46025 delete config.layout;
46027 if (layout.xtype && !layout.getEl) {
46028 // then layout needs constructing..
46029 layout = Roo.factory(layout, Roo);
46033 config.el = config.layout.getEl();
46035 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
46037 config.layout.monitorWindowResize = false; // turn off autosizing
46038 this.layout = config.layout;
46039 this.layout.getEl().addClass("roo-layout-nested-layout");
46040 this.layout.parent = this;
46047 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
46049 * @cfg {Roo.BorderLayout} layout The layout for this panel
46053 setSize : function(width, height){
46054 if(!this.ignoreResize(width, height)){
46055 var size = this.adjustForComponents(width, height);
46056 var el = this.layout.getEl();
46057 if (size.height < 1) {
46058 el.setWidth(size.width);
46060 el.setSize(size.width, size.height);
46062 var touch = el.dom.offsetWidth;
46063 this.layout.layout();
46064 // ie requires a double layout on the first pass
46065 if(Roo.isIE && !this.initialized){
46066 this.initialized = true;
46067 this.layout.layout();
46072 // activate all subpanels if not currently active..
46074 setActiveState : function(active){
46075 this.active = active;
46076 this.setActiveClass(active);
46079 this.fireEvent("deactivate", this);
46083 this.fireEvent("activate", this);
46084 // not sure if this should happen before or after..
46085 if (!this.layout) {
46086 return; // should not happen..
46089 for (var r in this.layout.regions) {
46090 reg = this.layout.getRegion(r);
46091 if (reg.getActivePanel()) {
46092 //reg.showPanel(reg.getActivePanel()); // force it to activate..
46093 reg.setActivePanel(reg.getActivePanel());
46096 if (!reg.panels.length) {
46099 reg.showPanel(reg.getPanel(0));
46108 * Returns the nested BorderLayout for this panel
46109 * @return {Roo.BorderLayout}
46111 getLayout : function(){
46112 return this.layout;
46116 * Adds a xtype elements to the layout of the nested panel
46120 xtype : 'ContentPanel',
46127 xtype : 'NestedLayoutPanel',
46133 items : [ ... list of content panels or nested layout panels.. ]
46137 * @param {Object} cfg Xtype definition of item to add.
46139 addxtype : function(cfg) {
46140 return this.layout.addxtype(cfg);
46145 * Ext JS Library 1.1.1
46146 * Copyright(c) 2006-2007, Ext JS, LLC.
46148 * Originally Released Under LGPL - original licence link has changed is not relivant.
46151 * <script type="text/javascript">
46154 * @class Roo.TabPanel
46155 * @extends Roo.util.Observable
46156 * A lightweight tab container.
46160 // basic tabs 1, built from existing content
46161 var tabs = new Roo.TabPanel("tabs1");
46162 tabs.addTab("script", "View Script");
46163 tabs.addTab("markup", "View Markup");
46164 tabs.activate("script");
46166 // more advanced tabs, built from javascript
46167 var jtabs = new Roo.TabPanel("jtabs");
46168 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
46170 // set up the UpdateManager
46171 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
46172 var updater = tab2.getUpdateManager();
46173 updater.setDefaultUrl("ajax1.htm");
46174 tab2.on('activate', updater.refresh, updater, true);
46176 // Use setUrl for Ajax loading
46177 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
46178 tab3.setUrl("ajax2.htm", null, true);
46181 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
46184 jtabs.activate("jtabs-1");
46187 * Create a new TabPanel.
46188 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
46189 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
46191 Roo.bootstrap.panel.Tabs = function(config){
46193 * The container element for this TabPanel.
46194 * @type Roo.Element
46196 this.el = Roo.get(config.el);
46199 if(typeof config == "boolean"){
46200 this.tabPosition = config ? "bottom" : "top";
46202 Roo.apply(this, config);
46206 if(this.tabPosition == "bottom"){
46207 // if tabs are at the bottom = create the body first.
46208 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46209 this.el.addClass("roo-tabs-bottom");
46211 // next create the tabs holders
46213 if (this.tabPosition == "west"){
46215 var reg = this.region; // fake it..
46217 if (!reg.mgr.parent) {
46220 reg = reg.mgr.parent.region;
46222 Roo.log("got nest?");
46224 if (reg.mgr.getRegion('west')) {
46225 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
46226 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
46227 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46228 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46229 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46237 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
46238 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
46239 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
46240 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
46245 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
46248 // finally - if tabs are at the top, then create the body last..
46249 if(this.tabPosition != "bottom"){
46250 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
46251 * @type Roo.Element
46253 this.bodyEl = Roo.get(this.createBody(this.el.dom));
46254 this.el.addClass("roo-tabs-top");
46258 this.bodyEl.setStyle("position", "relative");
46260 this.active = null;
46261 this.activateDelegate = this.activate.createDelegate(this);
46266 * Fires when the active tab changes
46267 * @param {Roo.TabPanel} this
46268 * @param {Roo.TabPanelItem} activePanel The new active tab
46272 * @event beforetabchange
46273 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
46274 * @param {Roo.TabPanel} this
46275 * @param {Object} e Set cancel to true on this object to cancel the tab change
46276 * @param {Roo.TabPanelItem} tab The tab being changed to
46278 "beforetabchange" : true
46281 Roo.EventManager.onWindowResize(this.onResize, this);
46282 this.cpad = this.el.getPadding("lr");
46283 this.hiddenCount = 0;
46286 // toolbar on the tabbar support...
46287 if (this.toolbar) {
46288 alert("no toolbar support yet");
46289 this.toolbar = false;
46291 var tcfg = this.toolbar;
46292 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
46293 this.toolbar = new Roo.Toolbar(tcfg);
46294 if (Roo.isSafari) {
46295 var tbl = tcfg.container.child('table', true);
46296 tbl.setAttribute('width', '100%');
46304 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46307 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46309 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46311 tabPosition : "top",
46313 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46315 currentTabWidth : 0,
46317 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46321 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46325 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46327 preferredTabWidth : 175,
46329 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46331 resizeTabs : false,
46333 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46335 monitorResize : true,
46337 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
46339 toolbar : false, // set by caller..
46341 region : false, /// set by caller
46343 disableTooltips : true, // not used yet...
46346 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46347 * @param {String} id The id of the div to use <b>or create</b>
46348 * @param {String} text The text for the tab
46349 * @param {String} content (optional) Content to put in the TabPanelItem body
46350 * @param {Boolean} closable (optional) True to create a close icon on the tab
46351 * @return {Roo.TabPanelItem} The created TabPanelItem
46353 addTab : function(id, text, content, closable, tpl)
46355 var item = new Roo.bootstrap.panel.TabItem({
46359 closable : closable,
46362 this.addTabItem(item);
46364 item.setContent(content);
46370 * Returns the {@link Roo.TabPanelItem} with the specified id/index
46371 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46372 * @return {Roo.TabPanelItem}
46374 getTab : function(id){
46375 return this.items[id];
46379 * Hides the {@link Roo.TabPanelItem} with the specified id/index
46380 * @param {String/Number} id The id or index of the TabPanelItem to hide.
46382 hideTab : function(id){
46383 var t = this.items[id];
46386 this.hiddenCount++;
46387 this.autoSizeTabs();
46392 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46393 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46395 unhideTab : function(id){
46396 var t = this.items[id];
46398 t.setHidden(false);
46399 this.hiddenCount--;
46400 this.autoSizeTabs();
46405 * Adds an existing {@link Roo.TabPanelItem}.
46406 * @param {Roo.TabPanelItem} item The TabPanelItem to add
46408 addTabItem : function(item)
46410 this.items[item.id] = item;
46411 this.items.push(item);
46412 this.autoSizeTabs();
46413 // if(this.resizeTabs){
46414 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46415 // this.autoSizeTabs();
46417 // item.autoSize();
46422 * Removes a {@link Roo.TabPanelItem}.
46423 * @param {String/Number} id The id or index of the TabPanelItem to remove.
46425 removeTab : function(id){
46426 var items = this.items;
46427 var tab = items[id];
46428 if(!tab) { return; }
46429 var index = items.indexOf(tab);
46430 if(this.active == tab && items.length > 1){
46431 var newTab = this.getNextAvailable(index);
46436 this.stripEl.dom.removeChild(tab.pnode.dom);
46437 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46438 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46440 items.splice(index, 1);
46441 delete this.items[tab.id];
46442 tab.fireEvent("close", tab);
46443 tab.purgeListeners();
46444 this.autoSizeTabs();
46447 getNextAvailable : function(start){
46448 var items = this.items;
46450 // look for a next tab that will slide over to
46451 // replace the one being removed
46452 while(index < items.length){
46453 var item = items[++index];
46454 if(item && !item.isHidden()){
46458 // if one isn't found select the previous tab (on the left)
46461 var item = items[--index];
46462 if(item && !item.isHidden()){
46470 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46471 * @param {String/Number} id The id or index of the TabPanelItem to disable.
46473 disableTab : function(id){
46474 var tab = this.items[id];
46475 if(tab && this.active != tab){
46481 * Enables a {@link Roo.TabPanelItem} that is disabled.
46482 * @param {String/Number} id The id or index of the TabPanelItem to enable.
46484 enableTab : function(id){
46485 var tab = this.items[id];
46490 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46491 * @param {String/Number} id The id or index of the TabPanelItem to activate.
46492 * @return {Roo.TabPanelItem} The TabPanelItem.
46494 activate : function(id)
46496 //Roo.log('activite:' + id);
46498 var tab = this.items[id];
46502 if(tab == this.active || tab.disabled){
46506 this.fireEvent("beforetabchange", this, e, tab);
46507 if(e.cancel !== true && !tab.disabled){
46509 this.active.hide();
46511 this.active = this.items[id];
46512 this.active.show();
46513 this.fireEvent("tabchange", this, this.active);
46519 * Gets the active {@link Roo.TabPanelItem}.
46520 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46522 getActiveTab : function(){
46523 return this.active;
46527 * Updates the tab body element to fit the height of the container element
46528 * for overflow scrolling
46529 * @param {Number} targetHeight (optional) Override the starting height from the elements height
46531 syncHeight : function(targetHeight){
46532 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46533 var bm = this.bodyEl.getMargins();
46534 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46535 this.bodyEl.setHeight(newHeight);
46539 onResize : function(){
46540 if(this.monitorResize){
46541 this.autoSizeTabs();
46546 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46548 beginUpdate : function(){
46549 this.updating = true;
46553 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46555 endUpdate : function(){
46556 this.updating = false;
46557 this.autoSizeTabs();
46561 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46563 autoSizeTabs : function()
46565 var count = this.items.length;
46566 var vcount = count - this.hiddenCount;
46569 this.stripEl.hide();
46571 this.stripEl.show();
46574 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46579 var w = Math.max(this.el.getWidth() - this.cpad, 10);
46580 var availWidth = Math.floor(w / vcount);
46581 var b = this.stripBody;
46582 if(b.getWidth() > w){
46583 var tabs = this.items;
46584 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46585 if(availWidth < this.minTabWidth){
46586 /*if(!this.sleft){ // incomplete scrolling code
46587 this.createScrollButtons();
46590 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46593 if(this.currentTabWidth < this.preferredTabWidth){
46594 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46600 * Returns the number of tabs in this TabPanel.
46603 getCount : function(){
46604 return this.items.length;
46608 * Resizes all the tabs to the passed width
46609 * @param {Number} The new width
46611 setTabWidth : function(width){
46612 this.currentTabWidth = width;
46613 for(var i = 0, len = this.items.length; i < len; i++) {
46614 if(!this.items[i].isHidden()) {
46615 this.items[i].setWidth(width);
46621 * Destroys this TabPanel
46622 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46624 destroy : function(removeEl){
46625 Roo.EventManager.removeResizeListener(this.onResize, this);
46626 for(var i = 0, len = this.items.length; i < len; i++){
46627 this.items[i].purgeListeners();
46629 if(removeEl === true){
46630 this.el.update("");
46635 createStrip : function(container)
46637 var strip = document.createElement("nav");
46638 strip.className = Roo.bootstrap.version == 4 ?
46639 "navbar-light bg-light" :
46640 "navbar navbar-default"; //"x-tabs-wrap";
46641 container.appendChild(strip);
46645 createStripList : function(strip)
46647 // div wrapper for retard IE
46648 // returns the "tr" element.
46649 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46650 //'<div class="x-tabs-strip-wrap">'+
46651 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46652 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46653 return strip.firstChild; //.firstChild.firstChild.firstChild;
46655 createBody : function(container)
46657 var body = document.createElement("div");
46658 Roo.id(body, "tab-body");
46659 //Roo.fly(body).addClass("x-tabs-body");
46660 Roo.fly(body).addClass("tab-content");
46661 container.appendChild(body);
46664 createItemBody :function(bodyEl, id){
46665 var body = Roo.getDom(id);
46667 body = document.createElement("div");
46670 //Roo.fly(body).addClass("x-tabs-item-body");
46671 Roo.fly(body).addClass("tab-pane");
46672 bodyEl.insertBefore(body, bodyEl.firstChild);
46676 createStripElements : function(stripEl, text, closable, tpl)
46678 var td = document.createElement("li"); // was td..
46679 td.className = 'nav-item';
46681 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46684 stripEl.appendChild(td);
46686 td.className = "x-tabs-closable";
46687 if(!this.closeTpl){
46688 this.closeTpl = new Roo.Template(
46689 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46690 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46691 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
46694 var el = this.closeTpl.overwrite(td, {"text": text});
46695 var close = el.getElementsByTagName("div")[0];
46696 var inner = el.getElementsByTagName("em")[0];
46697 return {"el": el, "close": close, "inner": inner};
46700 // not sure what this is..
46701 // if(!this.tabTpl){
46702 //this.tabTpl = new Roo.Template(
46703 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46704 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46706 // this.tabTpl = new Roo.Template(
46707 // '<a href="#">' +
46708 // '<span unselectable="on"' +
46709 // (this.disableTooltips ? '' : ' title="{text}"') +
46710 // ' >{text}</span></a>'
46716 var template = tpl || this.tabTpl || false;
46719 template = new Roo.Template(
46720 Roo.bootstrap.version == 4 ?
46722 '<a class="nav-link" href="#" unselectable="on"' +
46723 (this.disableTooltips ? '' : ' title="{text}"') +
46726 '<a class="nav-link" href="#">' +
46727 '<span unselectable="on"' +
46728 (this.disableTooltips ? '' : ' title="{text}"') +
46729 ' >{text}</span></a>'
46734 switch (typeof(template)) {
46738 template = new Roo.Template(template);
46744 var el = template.overwrite(td, {"text": text});
46746 var inner = el.getElementsByTagName("span")[0];
46748 return {"el": el, "inner": inner};
46756 * @class Roo.TabPanelItem
46757 * @extends Roo.util.Observable
46758 * Represents an individual item (tab plus body) in a TabPanel.
46759 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46760 * @param {String} id The id of this TabPanelItem
46761 * @param {String} text The text for the tab of this TabPanelItem
46762 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46764 Roo.bootstrap.panel.TabItem = function(config){
46766 * The {@link Roo.TabPanel} this TabPanelItem belongs to
46767 * @type Roo.TabPanel
46769 this.tabPanel = config.panel;
46771 * The id for this TabPanelItem
46774 this.id = config.id;
46776 this.disabled = false;
46778 this.text = config.text;
46780 this.loaded = false;
46781 this.closable = config.closable;
46784 * The body element for this TabPanelItem.
46785 * @type Roo.Element
46787 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46788 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46789 this.bodyEl.setStyle("display", "block");
46790 this.bodyEl.setStyle("zoom", "1");
46791 //this.hideAction();
46793 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46795 this.el = Roo.get(els.el);
46796 this.inner = Roo.get(els.inner, true);
46797 this.textEl = Roo.bootstrap.version == 4 ?
46798 this.el : Roo.get(this.el.dom.firstChild, true);
46800 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46801 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46804 // this.el.on("mousedown", this.onTabMouseDown, this);
46805 this.el.on("click", this.onTabClick, this);
46807 if(config.closable){
46808 var c = Roo.get(els.close, true);
46809 c.dom.title = this.closeText;
46810 c.addClassOnOver("close-over");
46811 c.on("click", this.closeClick, this);
46817 * Fires when this tab becomes the active tab.
46818 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46819 * @param {Roo.TabPanelItem} this
46823 * @event beforeclose
46824 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46825 * @param {Roo.TabPanelItem} this
46826 * @param {Object} e Set cancel to true on this object to cancel the close.
46828 "beforeclose": true,
46831 * Fires when this tab is closed.
46832 * @param {Roo.TabPanelItem} this
46836 * @event deactivate
46837 * Fires when this tab is no longer the active tab.
46838 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46839 * @param {Roo.TabPanelItem} this
46841 "deactivate" : true
46843 this.hidden = false;
46845 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46848 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46850 purgeListeners : function(){
46851 Roo.util.Observable.prototype.purgeListeners.call(this);
46852 this.el.removeAllListeners();
46855 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46858 this.status_node.addClass("active");
46861 this.tabPanel.stripWrap.repaint();
46863 this.fireEvent("activate", this.tabPanel, this);
46867 * Returns true if this tab is the active tab.
46868 * @return {Boolean}
46870 isActive : function(){
46871 return this.tabPanel.getActiveTab() == this;
46875 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46878 this.status_node.removeClass("active");
46880 this.fireEvent("deactivate", this.tabPanel, this);
46883 hideAction : function(){
46884 this.bodyEl.hide();
46885 this.bodyEl.setStyle("position", "absolute");
46886 this.bodyEl.setLeft("-20000px");
46887 this.bodyEl.setTop("-20000px");
46890 showAction : function(){
46891 this.bodyEl.setStyle("position", "relative");
46892 this.bodyEl.setTop("");
46893 this.bodyEl.setLeft("");
46894 this.bodyEl.show();
46898 * Set the tooltip for the tab.
46899 * @param {String} tooltip The tab's tooltip
46901 setTooltip : function(text){
46902 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46903 this.textEl.dom.qtip = text;
46904 this.textEl.dom.removeAttribute('title');
46906 this.textEl.dom.title = text;
46910 onTabClick : function(e){
46911 e.preventDefault();
46912 this.tabPanel.activate(this.id);
46915 onTabMouseDown : function(e){
46916 e.preventDefault();
46917 this.tabPanel.activate(this.id);
46920 getWidth : function(){
46921 return this.inner.getWidth();
46924 setWidth : function(width){
46925 var iwidth = width - this.linode.getPadding("lr");
46926 this.inner.setWidth(iwidth);
46927 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46928 this.linode.setWidth(width);
46932 * Show or hide the tab
46933 * @param {Boolean} hidden True to hide or false to show.
46935 setHidden : function(hidden){
46936 this.hidden = hidden;
46937 this.linode.setStyle("display", hidden ? "none" : "");
46941 * Returns true if this tab is "hidden"
46942 * @return {Boolean}
46944 isHidden : function(){
46945 return this.hidden;
46949 * Returns the text for this tab
46952 getText : function(){
46956 autoSize : function(){
46957 //this.el.beginMeasure();
46958 this.textEl.setWidth(1);
46960 * #2804 [new] Tabs in Roojs
46961 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46963 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46964 //this.el.endMeasure();
46968 * Sets the text for the tab (Note: this also sets the tooltip text)
46969 * @param {String} text The tab's text and tooltip
46971 setText : function(text){
46973 this.textEl.update(text);
46974 this.setTooltip(text);
46975 //if(!this.tabPanel.resizeTabs){
46976 // this.autoSize();
46980 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46982 activate : function(){
46983 this.tabPanel.activate(this.id);
46987 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46989 disable : function(){
46990 if(this.tabPanel.active != this){
46991 this.disabled = true;
46992 this.status_node.addClass("disabled");
46997 * Enables this TabPanelItem if it was previously disabled.
46999 enable : function(){
47000 this.disabled = false;
47001 this.status_node.removeClass("disabled");
47005 * Sets the content for this TabPanelItem.
47006 * @param {String} content The content
47007 * @param {Boolean} loadScripts true to look for and load scripts
47009 setContent : function(content, loadScripts){
47010 this.bodyEl.update(content, loadScripts);
47014 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
47015 * @return {Roo.UpdateManager} The UpdateManager
47017 getUpdateManager : function(){
47018 return this.bodyEl.getUpdateManager();
47022 * Set a URL to be used to load the content for this TabPanelItem.
47023 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
47024 * @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)
47025 * @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)
47026 * @return {Roo.UpdateManager} The UpdateManager
47028 setUrl : function(url, params, loadOnce){
47029 if(this.refreshDelegate){
47030 this.un('activate', this.refreshDelegate);
47032 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
47033 this.on("activate", this.refreshDelegate);
47034 return this.bodyEl.getUpdateManager();
47038 _handleRefresh : function(url, params, loadOnce){
47039 if(!loadOnce || !this.loaded){
47040 var updater = this.bodyEl.getUpdateManager();
47041 updater.update(url, params, this._setLoaded.createDelegate(this));
47046 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
47047 * Will fail silently if the setUrl method has not been called.
47048 * This does not activate the panel, just updates its content.
47050 refresh : function(){
47051 if(this.refreshDelegate){
47052 this.loaded = false;
47053 this.refreshDelegate();
47058 _setLoaded : function(){
47059 this.loaded = true;
47063 closeClick : function(e){
47066 this.fireEvent("beforeclose", this, o);
47067 if(o.cancel !== true){
47068 this.tabPanel.removeTab(this.id);
47072 * The text displayed in the tooltip for the close icon.
47075 closeText : "Close this tab"
47078 * This script refer to:
47079 * Title: International Telephone Input
47080 * Author: Jack O'Connor
47081 * Code version: v12.1.12
47082 * Availability: https://github.com/jackocnr/intl-tel-input.git
47085 Roo.bootstrap.form.PhoneInputData = function() {
47088 "Afghanistan (افغانستان)",
47093 "Albania (Shqipëri)",
47098 "Algeria (الجزائر)",
47123 "Antigua and Barbuda",
47133 "Armenia (Հայաստան)",
47149 "Austria (Österreich)",
47154 "Azerbaijan (Azərbaycan)",
47164 "Bahrain (البحرين)",
47169 "Bangladesh (বাংলাদেশ)",
47179 "Belarus (Беларусь)",
47184 "Belgium (België)",
47214 "Bosnia and Herzegovina (Босна и Херцеговина)",
47229 "British Indian Ocean Territory",
47234 "British Virgin Islands",
47244 "Bulgaria (България)",
47254 "Burundi (Uburundi)",
47259 "Cambodia (កម្ពុជា)",
47264 "Cameroon (Cameroun)",
47273 ["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"]
47276 "Cape Verde (Kabu Verdi)",
47281 "Caribbean Netherlands",
47292 "Central African Republic (République centrafricaine)",
47312 "Christmas Island",
47318 "Cocos (Keeling) Islands",
47329 "Comoros (جزر القمر)",
47334 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47339 "Congo (Republic) (Congo-Brazzaville)",
47359 "Croatia (Hrvatska)",
47380 "Czech Republic (Česká republika)",
47385 "Denmark (Danmark)",
47400 "Dominican Republic (República Dominicana)",
47404 ["809", "829", "849"]
47422 "Equatorial Guinea (Guinea Ecuatorial)",
47442 "Falkland Islands (Islas Malvinas)",
47447 "Faroe Islands (Føroyar)",
47468 "French Guiana (Guyane française)",
47473 "French Polynesia (Polynésie française)",
47488 "Georgia (საქართველო)",
47493 "Germany (Deutschland)",
47513 "Greenland (Kalaallit Nunaat)",
47550 "Guinea-Bissau (Guiné Bissau)",
47575 "Hungary (Magyarország)",
47580 "Iceland (Ísland)",
47600 "Iraq (العراق)",
47616 "Israel (ישראל)",
47643 "Jordan (الأردن)",
47648 "Kazakhstan (Казахстан)",
47669 "Kuwait (الكويت)",
47674 "Kyrgyzstan (Кыргызстан)",
47684 "Latvia (Latvija)",
47689 "Lebanon (لبنان)",
47704 "Libya (ليبيا)",
47714 "Lithuania (Lietuva)",
47729 "Macedonia (FYROM) (Македонија)",
47734 "Madagascar (Madagasikara)",
47764 "Marshall Islands",
47774 "Mauritania (موريتانيا)",
47779 "Mauritius (Moris)",
47800 "Moldova (Republica Moldova)",
47810 "Mongolia (Монгол)",
47815 "Montenegro (Crna Gora)",
47825 "Morocco (المغرب)",
47831 "Mozambique (Moçambique)",
47836 "Myanmar (Burma) (မြန်မာ)",
47841 "Namibia (Namibië)",
47856 "Netherlands (Nederland)",
47861 "New Caledonia (Nouvelle-Calédonie)",
47896 "North Korea (조선 민주주의 인민 공화국)",
47901 "Northern Mariana Islands",
47917 "Pakistan (پاکستان)",
47927 "Palestine (فلسطين)",
47937 "Papua New Guinea",
47979 "Réunion (La Réunion)",
47985 "Romania (România)",
48001 "Saint Barthélemy",
48012 "Saint Kitts and Nevis",
48022 "Saint Martin (Saint-Martin (partie française))",
48028 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
48033 "Saint Vincent and the Grenadines",
48048 "São Tomé and Príncipe (São Tomé e Príncipe)",
48053 "Saudi Arabia (المملكة العربية السعودية)",
48058 "Senegal (Sénégal)",
48088 "Slovakia (Slovensko)",
48093 "Slovenia (Slovenija)",
48103 "Somalia (Soomaaliya)",
48113 "South Korea (대한민국)",
48118 "South Sudan (جنوب السودان)",
48128 "Sri Lanka (ශ්රී ලංකාව)",
48133 "Sudan (السودان)",
48143 "Svalbard and Jan Mayen",
48154 "Sweden (Sverige)",
48159 "Switzerland (Schweiz)",
48164 "Syria (سوريا)",
48209 "Trinidad and Tobago",
48214 "Tunisia (تونس)",
48219 "Turkey (Türkiye)",
48229 "Turks and Caicos Islands",
48239 "U.S. Virgin Islands",
48249 "Ukraine (Україна)",
48254 "United Arab Emirates (الإمارات العربية المتحدة)",
48276 "Uzbekistan (Oʻzbekiston)",
48286 "Vatican City (Città del Vaticano)",
48297 "Vietnam (Việt Nam)",
48302 "Wallis and Futuna (Wallis-et-Futuna)",
48307 "Western Sahara (الصحراء الغربية)",
48313 "Yemen (اليمن)",
48337 * This script refer to:
48338 * Title: International Telephone Input
48339 * Author: Jack O'Connor
48340 * Code version: v12.1.12
48341 * Availability: https://github.com/jackocnr/intl-tel-input.git
48345 * @class Roo.bootstrap.form.PhoneInput
48346 * @extends Roo.bootstrap.form.TriggerField
48347 * An input with International dial-code selection
48349 * @cfg {String} defaultDialCode default '+852'
48350 * @cfg {Array} preferedCountries default []
48353 * Create a new PhoneInput.
48354 * @param {Object} config Configuration options
48357 Roo.bootstrap.form.PhoneInput = function(config) {
48358 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48361 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48363 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48365 listWidth: undefined,
48367 selectedClass: 'active',
48369 invalidClass : "has-warning",
48371 validClass: 'has-success',
48373 allowed: '0123456789',
48378 * @cfg {String} defaultDialCode The default dial code when initializing the input
48380 defaultDialCode: '+852',
48383 * @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
48385 preferedCountries: false,
48387 getAutoCreate : function()
48389 var data = Roo.bootstrap.form.PhoneInputData();
48390 var align = this.labelAlign || this.parentLabelAlign();
48393 this.allCountries = [];
48394 this.dialCodeMapping = [];
48396 for (var i = 0; i < data.length; i++) {
48398 this.allCountries[i] = {
48402 priority: c[3] || 0,
48403 areaCodes: c[4] || null
48405 this.dialCodeMapping[c[2]] = {
48408 priority: c[3] || 0,
48409 areaCodes: c[4] || null
48421 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48422 maxlength: this.max_length,
48423 cls : 'form-control tel-input',
48424 autocomplete: 'new-password'
48427 var hiddenInput = {
48430 cls: 'hidden-tel-input'
48434 hiddenInput.name = this.name;
48437 if (this.disabled) {
48438 input.disabled = true;
48441 var flag_container = {
48458 cls: this.hasFeedback ? 'has-feedback' : '',
48464 cls: 'dial-code-holder',
48471 cls: 'roo-select2-container input-group',
48478 if (this.fieldLabel.length) {
48481 tooltip: 'This field is required'
48487 cls: 'control-label',
48493 html: this.fieldLabel
48496 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48502 if(this.indicatorpos == 'right') {
48503 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48510 if(align == 'left') {
48518 if(this.labelWidth > 12){
48519 label.style = "width: " + this.labelWidth + 'px';
48521 if(this.labelWidth < 13 && this.labelmd == 0){
48522 this.labelmd = this.labelWidth;
48524 if(this.labellg > 0){
48525 label.cls += ' col-lg-' + this.labellg;
48526 input.cls += ' col-lg-' + (12 - this.labellg);
48528 if(this.labelmd > 0){
48529 label.cls += ' col-md-' + this.labelmd;
48530 container.cls += ' col-md-' + (12 - this.labelmd);
48532 if(this.labelsm > 0){
48533 label.cls += ' col-sm-' + this.labelsm;
48534 container.cls += ' col-sm-' + (12 - this.labelsm);
48536 if(this.labelxs > 0){
48537 label.cls += ' col-xs-' + this.labelxs;
48538 container.cls += ' col-xs-' + (12 - this.labelxs);
48548 var settings = this;
48550 ['xs','sm','md','lg'].map(function(size){
48551 if (settings[size]) {
48552 cfg.cls += ' col-' + size + '-' + settings[size];
48556 this.store = new Roo.data.Store({
48557 proxy : new Roo.data.MemoryProxy({}),
48558 reader : new Roo.data.JsonReader({
48569 'name' : 'dialCode',
48573 'name' : 'priority',
48577 'name' : 'areaCodes',
48584 if(!this.preferedCountries) {
48585 this.preferedCountries = [
48592 var p = this.preferedCountries.reverse();
48595 for (var i = 0; i < p.length; i++) {
48596 for (var j = 0; j < this.allCountries.length; j++) {
48597 if(this.allCountries[j].iso2 == p[i]) {
48598 var t = this.allCountries[j];
48599 this.allCountries.splice(j,1);
48600 this.allCountries.unshift(t);
48606 this.store.proxy.data = {
48608 data: this.allCountries
48614 initEvents : function()
48617 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48619 this.indicator = this.indicatorEl();
48620 this.flag = this.flagEl();
48621 this.dialCodeHolder = this.dialCodeHolderEl();
48623 this.trigger = this.el.select('div.flag-box',true).first();
48624 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48629 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48630 _this.list.setWidth(lw);
48633 this.list.on('mouseover', this.onViewOver, this);
48634 this.list.on('mousemove', this.onViewMove, this);
48635 this.inputEl().on("keyup", this.onKeyUp, this);
48636 this.inputEl().on("keypress", this.onKeyPress, this);
48638 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48640 this.view = new Roo.View(this.list, this.tpl, {
48641 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48644 this.view.on('click', this.onViewClick, this);
48645 this.setValue(this.defaultDialCode);
48648 onTriggerClick : function(e)
48650 Roo.log('trigger click');
48655 if(this.isExpanded()){
48657 this.hasFocus = false;
48659 this.store.load({});
48660 this.hasFocus = true;
48665 isExpanded : function()
48667 return this.list.isVisible();
48670 collapse : function()
48672 if(!this.isExpanded()){
48676 Roo.get(document).un('mousedown', this.collapseIf, this);
48677 Roo.get(document).un('mousewheel', this.collapseIf, this);
48678 this.fireEvent('collapse', this);
48682 expand : function()
48686 if(this.isExpanded() || !this.hasFocus){
48690 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48691 this.list.setWidth(lw);
48694 this.restrictHeight();
48696 Roo.get(document).on('mousedown', this.collapseIf, this);
48697 Roo.get(document).on('mousewheel', this.collapseIf, this);
48699 this.fireEvent('expand', this);
48702 restrictHeight : function()
48704 this.list.alignTo(this.inputEl(), this.listAlign);
48705 this.list.alignTo(this.inputEl(), this.listAlign);
48708 onViewOver : function(e, t)
48710 if(this.inKeyMode){
48713 var item = this.view.findItemFromChild(t);
48716 var index = this.view.indexOf(item);
48717 this.select(index, false);
48722 onViewClick : function(view, doFocus, el, e)
48724 var index = this.view.getSelectedIndexes()[0];
48726 var r = this.store.getAt(index);
48729 this.onSelect(r, index);
48731 if(doFocus !== false && !this.blockFocus){
48732 this.inputEl().focus();
48736 onViewMove : function(e, t)
48738 this.inKeyMode = false;
48741 select : function(index, scrollIntoView)
48743 this.selectedIndex = index;
48744 this.view.select(index);
48745 if(scrollIntoView !== false){
48746 var el = this.view.getNode(index);
48748 this.list.scrollChildIntoView(el, false);
48753 createList : function()
48755 this.list = Roo.get(document.body).createChild({
48757 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48758 style: 'display:none'
48761 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48764 collapseIf : function(e)
48766 var in_combo = e.within(this.el);
48767 var in_list = e.within(this.list);
48768 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48770 if (in_combo || in_list || is_list) {
48776 onSelect : function(record, index)
48778 if(this.fireEvent('beforeselect', this, record, index) !== false){
48780 this.setFlagClass(record.data.iso2);
48781 this.setDialCode(record.data.dialCode);
48782 this.hasFocus = false;
48784 this.fireEvent('select', this, record, index);
48788 flagEl : function()
48790 var flag = this.el.select('div.flag',true).first();
48797 dialCodeHolderEl : function()
48799 var d = this.el.select('input.dial-code-holder',true).first();
48806 setDialCode : function(v)
48808 this.dialCodeHolder.dom.value = '+'+v;
48811 setFlagClass : function(n)
48813 this.flag.dom.className = 'flag '+n;
48816 getValue : function()
48818 var v = this.inputEl().getValue();
48819 if(this.dialCodeHolder) {
48820 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48825 setValue : function(v)
48827 var d = this.getDialCode(v);
48829 //invalid dial code
48830 if(v.length == 0 || !d || d.length == 0) {
48832 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48833 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48839 this.setFlagClass(this.dialCodeMapping[d].iso2);
48840 this.setDialCode(d);
48841 this.inputEl().dom.value = v.replace('+'+d,'');
48842 this.hiddenEl().dom.value = this.getValue();
48847 getDialCode : function(v)
48851 if (v.length == 0) {
48852 return this.dialCodeHolder.dom.value;
48856 if (v.charAt(0) != "+") {
48859 var numericChars = "";
48860 for (var i = 1; i < v.length; i++) {
48861 var c = v.charAt(i);
48864 if (this.dialCodeMapping[numericChars]) {
48865 dialCode = v.substr(1, i);
48867 if (numericChars.length == 4) {
48877 this.setValue(this.defaultDialCode);
48881 hiddenEl : function()
48883 return this.el.select('input.hidden-tel-input',true).first();
48886 // after setting val
48887 onKeyUp : function(e){
48888 this.setValue(this.getValue());
48891 onKeyPress : function(e){
48892 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48899 * @class Roo.bootstrap.form.MoneyField
48900 * @extends Roo.bootstrap.form.ComboBox
48901 * Bootstrap MoneyField class
48904 * Create a new MoneyField.
48905 * @param {Object} config Configuration options
48908 Roo.bootstrap.form.MoneyField = function(config) {
48910 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48914 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48917 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48919 allowDecimals : true,
48921 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48923 decimalSeparator : ".",
48925 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48927 decimalPrecision : 0,
48929 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48931 allowNegative : true,
48933 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48937 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48939 minValue : Number.NEGATIVE_INFINITY,
48941 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48943 maxValue : Number.MAX_VALUE,
48945 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48947 minText : "The minimum value for this field is {0}",
48949 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48951 maxText : "The maximum value for this field is {0}",
48953 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48954 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48956 nanText : "{0} is not a valid number",
48958 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48962 * @cfg {String} defaults currency of the MoneyField
48963 * value should be in lkey
48965 defaultCurrency : false,
48967 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48969 thousandsDelimiter : false,
48971 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48980 * @cfg {Roo.data.Store} store Store to lookup currency??
48984 getAutoCreate : function()
48986 var align = this.labelAlign || this.parentLabelAlign();
48998 cls : 'form-control roo-money-amount-input',
48999 autocomplete: 'new-password'
49002 var hiddenInput = {
49006 cls: 'hidden-number-input'
49009 if(this.max_length) {
49010 input.maxlength = this.max_length;
49014 hiddenInput.name = this.name;
49017 if (this.disabled) {
49018 input.disabled = true;
49021 var clg = 12 - this.inputlg;
49022 var cmd = 12 - this.inputmd;
49023 var csm = 12 - this.inputsm;
49024 var cxs = 12 - this.inputxs;
49028 cls : 'row roo-money-field',
49032 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
49036 cls: 'roo-select2-container input-group',
49040 cls : 'form-control roo-money-currency-input',
49041 autocomplete: 'new-password',
49043 name : this.currencyName
49047 cls : 'input-group-addon',
49061 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
49065 cls: this.hasFeedback ? 'has-feedback' : '',
49076 if (this.fieldLabel.length) {
49079 tooltip: 'This field is required'
49085 cls: 'control-label',
49091 html: this.fieldLabel
49094 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
49100 if(this.indicatorpos == 'right') {
49101 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
49108 if(align == 'left') {
49116 if(this.labelWidth > 12){
49117 label.style = "width: " + this.labelWidth + 'px';
49119 if(this.labelWidth < 13 && this.labelmd == 0){
49120 this.labelmd = this.labelWidth;
49122 if(this.labellg > 0){
49123 label.cls += ' col-lg-' + this.labellg;
49124 input.cls += ' col-lg-' + (12 - this.labellg);
49126 if(this.labelmd > 0){
49127 label.cls += ' col-md-' + this.labelmd;
49128 container.cls += ' col-md-' + (12 - this.labelmd);
49130 if(this.labelsm > 0){
49131 label.cls += ' col-sm-' + this.labelsm;
49132 container.cls += ' col-sm-' + (12 - this.labelsm);
49134 if(this.labelxs > 0){
49135 label.cls += ' col-xs-' + this.labelxs;
49136 container.cls += ' col-xs-' + (12 - this.labelxs);
49147 var settings = this;
49149 ['xs','sm','md','lg'].map(function(size){
49150 if (settings[size]) {
49151 cfg.cls += ' col-' + size + '-' + settings[size];
49158 initEvents : function()
49160 this.indicator = this.indicatorEl();
49162 this.initCurrencyEvent();
49164 this.initNumberEvent();
49167 initCurrencyEvent : function()
49170 throw "can not find store for combo";
49173 this.store = Roo.factory(this.store, Roo.data);
49174 this.store.parent = this;
49178 this.triggerEl = this.el.select('.input-group-addon', true).first();
49180 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
49185 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
49186 _this.list.setWidth(lw);
49189 this.list.on('mouseover', this.onViewOver, this);
49190 this.list.on('mousemove', this.onViewMove, this);
49191 this.list.on('scroll', this.onViewScroll, this);
49194 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
49197 this.view = new Roo.View(this.list, this.tpl, {
49198 singleSelect:true, store: this.store, selectedClass: this.selectedClass
49201 this.view.on('click', this.onViewClick, this);
49203 this.store.on('beforeload', this.onBeforeLoad, this);
49204 this.store.on('load', this.onLoad, this);
49205 this.store.on('loadexception', this.onLoadException, this);
49207 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
49208 "up" : function(e){
49209 this.inKeyMode = true;
49213 "down" : function(e){
49214 if(!this.isExpanded()){
49215 this.onTriggerClick();
49217 this.inKeyMode = true;
49222 "enter" : function(e){
49225 if(this.fireEvent("specialkey", this, e)){
49226 this.onViewClick(false);
49232 "esc" : function(e){
49236 "tab" : function(e){
49239 if(this.fireEvent("specialkey", this, e)){
49240 this.onViewClick(false);
49248 doRelay : function(foo, bar, hname){
49249 if(hname == 'down' || this.scope.isExpanded()){
49250 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
49258 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
49262 initNumberEvent : function(e)
49264 this.inputEl().on("keydown" , this.fireKey, this);
49265 this.inputEl().on("focus", this.onFocus, this);
49266 this.inputEl().on("blur", this.onBlur, this);
49268 this.inputEl().relayEvent('keyup', this);
49270 if(this.indicator){
49271 this.indicator.addClass('invisible');
49274 this.originalValue = this.getValue();
49276 if(this.validationEvent == 'keyup'){
49277 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
49278 this.inputEl().on('keyup', this.filterValidation, this);
49280 else if(this.validationEvent !== false){
49281 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
49284 if(this.selectOnFocus){
49285 this.on("focus", this.preFocus, this);
49288 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49289 this.inputEl().on("keypress", this.filterKeys, this);
49291 this.inputEl().relayEvent('keypress', this);
49294 var allowed = "0123456789";
49296 if(this.allowDecimals){
49297 allowed += this.decimalSeparator;
49300 if(this.allowNegative){
49304 if(this.thousandsDelimiter) {
49308 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49310 var keyPress = function(e){
49312 var k = e.getKey();
49314 var c = e.getCharCode();
49317 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49318 allowed.indexOf(String.fromCharCode(c)) === -1
49324 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49328 if(allowed.indexOf(String.fromCharCode(c)) === -1){
49333 this.inputEl().on("keypress", keyPress, this);
49337 onTriggerClick : function(e)
49344 this.loadNext = false;
49346 if(this.isExpanded()){
49351 this.hasFocus = true;
49353 if(this.triggerAction == 'all') {
49354 this.doQuery(this.allQuery, true);
49358 this.doQuery(this.getRawValue());
49361 getCurrency : function()
49363 var v = this.currencyEl().getValue();
49368 restrictHeight : function()
49370 this.list.alignTo(this.currencyEl(), this.listAlign);
49371 this.list.alignTo(this.currencyEl(), this.listAlign);
49374 onViewClick : function(view, doFocus, el, e)
49376 var index = this.view.getSelectedIndexes()[0];
49378 var r = this.store.getAt(index);
49381 this.onSelect(r, index);
49385 onSelect : function(record, index){
49387 if(this.fireEvent('beforeselect', this, record, index) !== false){
49389 this.setFromCurrencyData(index > -1 ? record.data : false);
49393 this.fireEvent('select', this, record, index);
49397 setFromCurrencyData : function(o)
49401 this.lastCurrency = o;
49403 if (this.currencyField) {
49404 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49406 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
49409 this.lastSelectionText = currency;
49411 //setting default currency
49412 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49413 this.setCurrency(this.defaultCurrency);
49417 this.setCurrency(currency);
49420 setFromData : function(o)
49424 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49426 this.setFromCurrencyData(c);
49431 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49433 Roo.log('no value set for '+ (this.name ? this.name : this.id));
49436 this.setValue(value);
49440 setCurrency : function(v)
49442 this.currencyValue = v;
49445 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49450 setValue : function(v)
49452 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49458 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49460 this.inputEl().dom.value = (v == '') ? '' :
49461 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49463 if(!this.allowZero && v === '0') {
49464 this.hiddenEl().dom.value = '';
49465 this.inputEl().dom.value = '';
49472 getRawValue : function()
49474 var v = this.inputEl().getValue();
49479 getValue : function()
49481 return this.fixPrecision(this.parseValue(this.getRawValue()));
49484 parseValue : function(value)
49486 if(this.thousandsDelimiter) {
49488 r = new RegExp(",", "g");
49489 value = value.replace(r, "");
49492 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49493 return isNaN(value) ? '' : value;
49497 fixPrecision : function(value)
49499 if(this.thousandsDelimiter) {
49501 r = new RegExp(",", "g");
49502 value = value.replace(r, "");
49505 var nan = isNaN(value);
49507 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49508 return nan ? '' : value;
49510 return parseFloat(value).toFixed(this.decimalPrecision);
49513 decimalPrecisionFcn : function(v)
49515 return Math.floor(v);
49518 validateValue : function(value)
49520 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49524 var num = this.parseValue(value);
49527 this.markInvalid(String.format(this.nanText, value));
49531 if(num < this.minValue){
49532 this.markInvalid(String.format(this.minText, this.minValue));
49536 if(num > this.maxValue){
49537 this.markInvalid(String.format(this.maxText, this.maxValue));
49544 validate : function()
49546 if(this.disabled || this.allowBlank){
49551 var currency = this.getCurrency();
49553 if(this.validateValue(this.getRawValue()) && currency.length){
49558 this.markInvalid();
49562 getName: function()
49567 beforeBlur : function()
49573 var v = this.parseValue(this.getRawValue());
49580 onBlur : function()
49584 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49585 //this.el.removeClass(this.focusClass);
49588 this.hasFocus = false;
49590 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49594 var v = this.getValue();
49596 if(String(v) !== String(this.startValue)){
49597 this.fireEvent('change', this, v, this.startValue);
49600 this.fireEvent("blur", this);
49603 inputEl : function()
49605 return this.el.select('.roo-money-amount-input', true).first();
49608 currencyEl : function()
49610 return this.el.select('.roo-money-currency-input', true).first();
49613 hiddenEl : function()
49615 return this.el.select('input.hidden-number-input',true).first();
49619 * @class Roo.bootstrap.BezierSignature
49620 * @extends Roo.bootstrap.Component
49621 * Bootstrap BezierSignature class
49622 * This script refer to:
49623 * Title: Signature Pad
49625 * Availability: https://github.com/szimek/signature_pad
49628 * Create a new BezierSignature
49629 * @param {Object} config The config object
49632 Roo.bootstrap.BezierSignature = function(config){
49633 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49639 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49646 mouse_btn_down: true,
49649 * @cfg {int} canvas height
49651 canvas_height: '200px',
49654 * @cfg {float|function} Radius of a single dot.
49659 * @cfg {float} Minimum width of a line. Defaults to 0.5.
49664 * @cfg {float} Maximum width of a line. Defaults to 2.5.
49669 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49674 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49679 * @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.
49681 bg_color: 'rgba(0, 0, 0, 0)',
49684 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49686 dot_color: 'black',
49689 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49691 velocity_filter_weight: 0.7,
49694 * @cfg {function} Callback when stroke begin.
49699 * @cfg {function} Callback when stroke end.
49703 getAutoCreate : function()
49705 var cls = 'roo-signature column';
49708 cls += ' ' + this.cls;
49718 for(var i = 0; i < col_sizes.length; i++) {
49719 if(this[col_sizes[i]]) {
49720 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49730 cls: 'roo-signature-body',
49734 cls: 'roo-signature-body-canvas',
49735 height: this.canvas_height,
49736 width: this.canvas_width
49743 style: 'display: none'
49751 initEvents: function()
49753 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49755 var canvas = this.canvasEl();
49757 // mouse && touch event swapping...
49758 canvas.dom.style.touchAction = 'none';
49759 canvas.dom.style.msTouchAction = 'none';
49761 this.mouse_btn_down = false;
49762 canvas.on('mousedown', this._handleMouseDown, this);
49763 canvas.on('mousemove', this._handleMouseMove, this);
49764 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49766 if (window.PointerEvent) {
49767 canvas.on('pointerdown', this._handleMouseDown, this);
49768 canvas.on('pointermove', this._handleMouseMove, this);
49769 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49772 if ('ontouchstart' in window) {
49773 canvas.on('touchstart', this._handleTouchStart, this);
49774 canvas.on('touchmove', this._handleTouchMove, this);
49775 canvas.on('touchend', this._handleTouchEnd, this);
49778 Roo.EventManager.onWindowResize(this.resize, this, true);
49780 // file input event
49781 this.fileEl().on('change', this.uploadImage, this);
49788 resize: function(){
49790 var canvas = this.canvasEl().dom;
49791 var ctx = this.canvasElCtx();
49792 var img_data = false;
49794 if(canvas.width > 0) {
49795 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49797 // setting canvas width will clean img data
49800 var style = window.getComputedStyle ?
49801 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49803 var padding_left = parseInt(style.paddingLeft) || 0;
49804 var padding_right = parseInt(style.paddingRight) || 0;
49806 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49809 ctx.putImageData(img_data, 0, 0);
49813 _handleMouseDown: function(e)
49815 if (e.browserEvent.which === 1) {
49816 this.mouse_btn_down = true;
49817 this.strokeBegin(e);
49821 _handleMouseMove: function (e)
49823 if (this.mouse_btn_down) {
49824 this.strokeMoveUpdate(e);
49828 _handleMouseUp: function (e)
49830 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49831 this.mouse_btn_down = false;
49836 _handleTouchStart: function (e) {
49838 e.preventDefault();
49839 if (e.browserEvent.targetTouches.length === 1) {
49840 // var touch = e.browserEvent.changedTouches[0];
49841 // this.strokeBegin(touch);
49843 this.strokeBegin(e); // assume e catching the correct xy...
49847 _handleTouchMove: function (e) {
49848 e.preventDefault();
49849 // var touch = event.targetTouches[0];
49850 // _this._strokeMoveUpdate(touch);
49851 this.strokeMoveUpdate(e);
49854 _handleTouchEnd: function (e) {
49855 var wasCanvasTouched = e.target === this.canvasEl().dom;
49856 if (wasCanvasTouched) {
49857 e.preventDefault();
49858 // var touch = event.changedTouches[0];
49859 // _this._strokeEnd(touch);
49864 reset: function () {
49865 this._lastPoints = [];
49866 this._lastVelocity = 0;
49867 this._lastWidth = (this.min_width + this.max_width) / 2;
49868 this.canvasElCtx().fillStyle = this.dot_color;
49871 strokeMoveUpdate: function(e)
49873 this.strokeUpdate(e);
49875 if (this.throttle) {
49876 this.throttleStroke(this.strokeUpdate, this.throttle);
49879 this.strokeUpdate(e);
49883 strokeBegin: function(e)
49885 var newPointGroup = {
49886 color: this.dot_color,
49890 if (typeof this.onBegin === 'function') {
49894 this.curve_data.push(newPointGroup);
49896 this.strokeUpdate(e);
49899 strokeUpdate: function(e)
49901 var rect = this.canvasEl().dom.getBoundingClientRect();
49902 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49903 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49904 var lastPoints = lastPointGroup.points;
49905 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49906 var isLastPointTooClose = lastPoint
49907 ? point.distanceTo(lastPoint) <= this.min_distance
49909 var color = lastPointGroup.color;
49910 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49911 var curve = this.addPoint(point);
49913 this.drawDot({color: color, point: point});
49916 this.drawCurve({color: color, curve: curve});
49926 strokeEnd: function(e)
49928 this.strokeUpdate(e);
49929 if (typeof this.onEnd === 'function') {
49934 addPoint: function (point) {
49935 var _lastPoints = this._lastPoints;
49936 _lastPoints.push(point);
49937 if (_lastPoints.length > 2) {
49938 if (_lastPoints.length === 3) {
49939 _lastPoints.unshift(_lastPoints[0]);
49941 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49942 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49943 _lastPoints.shift();
49949 calculateCurveWidths: function (startPoint, endPoint) {
49950 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49951 (1 - this.velocity_filter_weight) * this._lastVelocity;
49953 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49956 start: this._lastWidth
49959 this._lastVelocity = velocity;
49960 this._lastWidth = newWidth;
49964 drawDot: function (_a) {
49965 var color = _a.color, point = _a.point;
49966 var ctx = this.canvasElCtx();
49967 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49969 this.drawCurveSegment(point.x, point.y, width);
49971 ctx.fillStyle = color;
49975 drawCurve: function (_a) {
49976 var color = _a.color, curve = _a.curve;
49977 var ctx = this.canvasElCtx();
49978 var widthDelta = curve.endWidth - curve.startWidth;
49979 var drawSteps = Math.floor(curve.length()) * 2;
49981 ctx.fillStyle = color;
49982 for (var i = 0; i < drawSteps; i += 1) {
49983 var t = i / drawSteps;
49989 var x = uuu * curve.startPoint.x;
49990 x += 3 * uu * t * curve.control1.x;
49991 x += 3 * u * tt * curve.control2.x;
49992 x += ttt * curve.endPoint.x;
49993 var y = uuu * curve.startPoint.y;
49994 y += 3 * uu * t * curve.control1.y;
49995 y += 3 * u * tt * curve.control2.y;
49996 y += ttt * curve.endPoint.y;
49997 var width = curve.startWidth + ttt * widthDelta;
49998 this.drawCurveSegment(x, y, width);
50004 drawCurveSegment: function (x, y, width) {
50005 var ctx = this.canvasElCtx();
50007 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
50008 this.is_empty = false;
50013 var ctx = this.canvasElCtx();
50014 var canvas = this.canvasEl().dom;
50015 ctx.fillStyle = this.bg_color;
50016 ctx.clearRect(0, 0, canvas.width, canvas.height);
50017 ctx.fillRect(0, 0, canvas.width, canvas.height);
50018 this.curve_data = [];
50020 this.is_empty = true;
50025 return this.el.select('input',true).first();
50028 canvasEl: function()
50030 return this.el.select('canvas',true).first();
50033 canvasElCtx: function()
50035 return this.el.select('canvas',true).first().dom.getContext('2d');
50038 getImage: function(type)
50040 if(this.is_empty) {
50045 return this.canvasEl().dom.toDataURL('image/'+type, 1);
50048 drawFromImage: function(img_src)
50050 var img = new Image();
50052 img.onload = function(){
50053 this.canvasElCtx().drawImage(img, 0, 0);
50058 this.is_empty = false;
50061 selectImage: function()
50063 this.fileEl().dom.click();
50066 uploadImage: function(e)
50068 var reader = new FileReader();
50070 reader.onload = function(e){
50071 var img = new Image();
50072 img.onload = function(){
50074 this.canvasElCtx().drawImage(img, 0, 0);
50076 img.src = e.target.result;
50079 reader.readAsDataURL(e.target.files[0]);
50082 // Bezier Point Constructor
50083 Point: (function () {
50084 function Point(x, y, time) {
50087 this.time = time || Date.now();
50089 Point.prototype.distanceTo = function (start) {
50090 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
50092 Point.prototype.equals = function (other) {
50093 return this.x === other.x && this.y === other.y && this.time === other.time;
50095 Point.prototype.velocityFrom = function (start) {
50096 return this.time !== start.time
50097 ? this.distanceTo(start) / (this.time - start.time)
50104 // Bezier Constructor
50105 Bezier: (function () {
50106 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
50107 this.startPoint = startPoint;
50108 this.control2 = control2;
50109 this.control1 = control1;
50110 this.endPoint = endPoint;
50111 this.startWidth = startWidth;
50112 this.endWidth = endWidth;
50114 Bezier.fromPoints = function (points, widths, scope) {
50115 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
50116 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
50117 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
50119 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
50120 var dx1 = s1.x - s2.x;
50121 var dy1 = s1.y - s2.y;
50122 var dx2 = s2.x - s3.x;
50123 var dy2 = s2.y - s3.y;
50124 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
50125 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
50126 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
50127 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
50128 var dxm = m1.x - m2.x;
50129 var dym = m1.y - m2.y;
50130 var k = l2 / (l1 + l2);
50131 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
50132 var tx = s2.x - cm.x;
50133 var ty = s2.y - cm.y;
50135 c1: new scope.Point(m1.x + tx, m1.y + ty),
50136 c2: new scope.Point(m2.x + tx, m2.y + ty)
50139 Bezier.prototype.length = function () {
50144 for (var i = 0; i <= steps; i += 1) {
50146 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
50147 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
50149 var xdiff = cx - px;
50150 var ydiff = cy - py;
50151 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
50158 Bezier.prototype.point = function (t, start, c1, c2, end) {
50159 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
50160 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
50161 + (3.0 * c2 * (1.0 - t) * t * t)
50162 + (end * t * t * t);
50167 throttleStroke: function(fn, wait) {
50168 if (wait === void 0) { wait = 250; }
50170 var timeout = null;
50174 var later = function () {
50175 previous = Date.now();
50177 result = fn.apply(storedContext, storedArgs);
50179 storedContext = null;
50183 return function wrapper() {
50185 for (var _i = 0; _i < arguments.length; _i++) {
50186 args[_i] = arguments[_i];
50188 var now = Date.now();
50189 var remaining = wait - (now - previous);
50190 storedContext = this;
50192 if (remaining <= 0 || remaining > wait) {
50194 clearTimeout(timeout);
50198 result = fn.apply(storedContext, storedArgs);
50200 storedContext = null;
50204 else if (!timeout) {
50205 timeout = window.setTimeout(later, remaining);
50215 // old names for form elements
50216 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
50217 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
50218 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
50219 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
50220 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
50221 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
50222 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
50223 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
50224 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
50225 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
50226 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
50227 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
50228 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
50229 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
50230 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
50231 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
50232 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
50233 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
50234 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
50235 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
50236 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
50237 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
50238 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
50239 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
50240 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
50241 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
50243 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
50244 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
50246 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
50247 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
50249 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
50250 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
50251 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
50252 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator