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;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<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>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
366 * Fetch the element to display the tooltip on.
367 * @return {Roo.Element} defaults to this.el
369 tooltipEl : function()
374 addxtype : function(tree,cntr)
378 cn = Roo.factory(tree);
379 //Roo.log(['addxtype', cn]);
381 cn.parentType = this.xtype; //??
382 cn.parentId = this.id;
384 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385 if (typeof(cn.container_method) == 'string') {
386 cntr = cn.container_method;
390 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
392 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
394 var build_from_html = Roo.XComponent.build_from_html;
396 var is_body = (tree.xtype == 'Body') ;
398 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
400 var self_cntr_el = Roo.get(this[cntr](false));
402 // do not try and build conditional elements
403 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
407 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409 return this.addxtypeChild(tree,cntr, is_body);
412 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
415 return this.addxtypeChild(Roo.apply({}, tree),cntr);
418 Roo.log('skipping render');
424 if (!build_from_html) {
428 // this i think handles overlaying multiple children of the same type
429 // with the sam eelement.. - which might be buggy..
431 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
441 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448 addxtypeChild : function (tree, cntr, is_body)
450 Roo.debug && Roo.log('addxtypeChild:' + cntr);
452 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
455 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456 (typeof(tree['flexy:foreach']) != 'undefined');
460 skip_children = false;
461 // render the element if it's not BODY.
464 // if parent was disabled, then do not try and create the children..
465 if(!this[cntr](true)){
470 cn = Roo.factory(tree);
472 cn.parentType = this.xtype; //??
473 cn.parentId = this.id;
475 var build_from_html = Roo.XComponent.build_from_html;
478 // does the container contain child eleemnts with 'xtype' attributes.
479 // that match this xtype..
480 // note - when we render we create these as well..
481 // so we should check to see if body has xtype set.
482 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
484 var self_cntr_el = Roo.get(this[cntr](false));
485 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
487 //Roo.log(Roo.XComponent.build_from_html);
488 //Roo.log("got echild:");
491 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492 // and are not displayed -this causes this to use up the wrong element when matching.
493 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
496 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503 //echild.dom.removeAttribute('xtype');
505 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506 Roo.debug && Roo.log(self_cntr_el);
507 Roo.debug && Roo.log(echild);
508 Roo.debug && Roo.log(cn);
514 // if object has flexy:if - then it may or may not be rendered.
515 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
516 // skip a flexy if element.
517 Roo.debug && Roo.log('skipping render');
518 Roo.debug && Roo.log(tree);
520 Roo.debug && Roo.log('skipping all children');
521 skip_children = true;
526 // actually if flexy:foreach is found, we really want to create
527 // multiple copies here...
529 //Roo.log(this[cntr]());
530 // some elements do not have render methods.. like the layouts...
532 if(this[cntr](true) === false){
537 cn.render && cn.render(this[cntr](true));
540 // then add the element..
547 if (typeof (tree.menu) != 'undefined') {
548 tree.menu.parentType = cn.xtype;
549 tree.menu.triggerEl = cn.el;
550 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
554 if (!tree.items || !tree.items.length) {
556 //Roo.log(["no children", this]);
561 var items = tree.items;
564 //Roo.log(items.length);
566 if (!skip_children) {
567 for(var i =0;i < items.length;i++) {
568 // Roo.log(['add child', items[i]]);
569 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575 //Roo.log("fire childrenrendered");
577 cn.fireEvent('childrenrendered', this);
583 * Set the element that will be used to show or hide
585 setVisibilityEl : function(el)
587 this.visibilityEl = el;
591 * Get the element that will be used to show or hide
593 getVisibilityEl : function()
595 if (typeof(this.visibilityEl) == 'object') {
596 return this.visibilityEl;
599 if (typeof(this.visibilityEl) == 'string') {
600 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607 * Show a component - removes 'hidden' class
611 if(!this.getVisibilityEl()){
615 this.getVisibilityEl().removeClass(['hidden','d-none']);
617 this.fireEvent('show', this);
622 * Hide a component - adds 'hidden' class
626 if(!this.getVisibilityEl()){
630 this.getVisibilityEl().addClass(['hidden','d-none']);
632 this.fireEvent('hide', this);
645 * @class Roo.bootstrap.Element
646 * @extends Roo.bootstrap.Component
647 * Bootstrap Element class
648 * @cfg {String} html contents of the element
649 * @cfg {String} tag tag of the element
650 * @cfg {String} cls class of the element
651 * @cfg {Boolean} preventDefault (true|false) default false
652 * @cfg {Boolean} clickable (true|false) default false
653 * @cfg {String} role default blank - set to button to force cursor pointer
657 * Create a new Element
658 * @param {Object} config The config object
661 Roo.bootstrap.Element = function(config){
662 Roo.bootstrap.Element.superclass.constructor.call(this, config);
668 * When a element is chick
669 * @param {Roo.bootstrap.Element} this
670 * @param {Roo.EventObject} e
678 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
683 preventDefault: false,
688 getAutoCreate : function(){
692 // cls: this.cls, double assign in parent class Component.js :: onRender
695 if (this.role !== false) {
696 cfg.role = this.role;
702 initEvents: function()
704 Roo.bootstrap.Element.superclass.initEvents.call(this);
707 this.el.on('click', this.onClick, this);
713 onClick : function(e)
715 if(this.preventDefault){
719 this.fireEvent('click', this, e); // why was this double click before?
727 getValue : function()
729 return this.el.dom.innerHTML;
732 setValue : function(value)
734 this.el.dom.innerHTML = value;
749 * @class Roo.bootstrap.DropTarget
750 * @extends Roo.bootstrap.Element
751 * Bootstrap DropTarget class
753 * @cfg {string} name dropable name
756 * Create a new Dropable Area
757 * @param {Object} config The config object
760 Roo.bootstrap.DropTarget = function(config){
761 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767 * When a element is chick
768 * @param {Roo.bootstrap.Element} this
769 * @param {Roo.EventObject} e
775 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
778 getAutoCreate : function(){
783 initEvents: function()
785 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
786 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
789 drop : this.dragDrop.createDelegate(this),
790 enter : this.dragEnter.createDelegate(this),
791 out : this.dragOut.createDelegate(this),
792 over : this.dragOver.createDelegate(this)
796 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
799 dragDrop : function(source,e,data)
801 // user has to decide how to impliment this.
804 //this.fireEvent('drop', this, source, e ,data);
808 dragEnter : function(n, dd, e, data)
810 // probably want to resize the element to match the dropped element..
812 this.originalSize = this.el.getSize();
813 this.el.setSize( n.el.getSize());
814 this.dropZone.DDM.refreshCache(this.name);
815 Roo.log([n, dd, e, data]);
818 dragOut : function(value)
820 // resize back to normal
822 this.el.setSize(this.originalSize);
823 this.dropZone.resetConstraints();
826 dragOver : function()
843 * @class Roo.bootstrap.Body
844 * @extends Roo.bootstrap.Component
845 * Bootstrap Body class
849 * @param {Object} config The config object
852 Roo.bootstrap.Body = function(config){
854 config = config || {};
856 Roo.bootstrap.Body.superclass.constructor.call(this, config);
857 this.el = Roo.get(config.el ? config.el : document.body );
858 if (this.cls && this.cls.length) {
859 Roo.get(document.body).addClass(this.cls);
863 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
865 is_body : true,// just to make sure it's constructed?
870 onRender : function(ct, position)
872 /* Roo.log("Roo.bootstrap.Body - onRender");
873 if (this.cls && this.cls.length) {
874 Roo.get(document.body).addClass(this.cls);
893 * @class Roo.bootstrap.ButtonGroup
894 * @extends Roo.bootstrap.Component
895 * Bootstrap ButtonGroup class
896 * @cfg {String} size lg | sm | xs (default empty normal)
897 * @cfg {String} align vertical | justified (default none)
898 * @cfg {String} direction up | down (default down)
899 * @cfg {Boolean} toolbar false | true
900 * @cfg {Boolean} btn true | false
905 * @param {Object} config The config object
908 Roo.bootstrap.ButtonGroup = function(config){
909 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
912 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
920 getAutoCreate : function(){
926 cfg.html = this.html || cfg.html;
937 if (['vertical','justified'].indexOf(this.align)!==-1) {
938 cfg.cls = 'btn-group-' + this.align;
940 if (this.align == 'justified') {
941 console.log(this.items);
945 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
946 cfg.cls += ' btn-group-' + this.size;
949 if (this.direction == 'up') {
950 cfg.cls += ' dropup' ;
956 * Add a button to the group (similar to NavItem API.)
958 addItem : function(cfg)
960 var cn = new Roo.bootstrap.Button(cfg);
962 cn.parentId = this.id;
963 cn.onRender(this.el, null);
977 * @class Roo.bootstrap.Button
978 * @extends Roo.bootstrap.Component
979 * Bootstrap Button class
980 * @cfg {String} html The button content
981 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
982 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
983 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
984 * @cfg {String} size (lg|sm|xs)
985 * @cfg {String} tag (a|input|submit)
986 * @cfg {String} href empty or href
987 * @cfg {Boolean} disabled default false;
988 * @cfg {Boolean} isClose default false;
989 * @cfg {String} glyphicon depricated - use fa
990 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
991 * @cfg {String} badge text for badge
992 * @cfg {String} theme (default|glow)
993 * @cfg {Boolean} inverse dark themed version
994 * @cfg {Boolean} toggle is it a slidy toggle button
995 * @cfg {Boolean} pressed default null - if the button ahs active state
996 * @cfg {String} ontext text for on slidy toggle state
997 * @cfg {String} offtext text for off slidy toggle state
998 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
999 * @cfg {Boolean} removeClass remove the standard class..
1000 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1001 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1004 * Create a new button
1005 * @param {Object} config The config object
1009 Roo.bootstrap.Button = function(config){
1010 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016 * When a button is pressed
1017 * @param {Roo.bootstrap.Button} btn
1018 * @param {Roo.EventObject} e
1023 * When a button is double clicked
1024 * @param {Roo.bootstrap.Button} btn
1025 * @param {Roo.EventObject} e
1030 * After the button has been toggles
1031 * @param {Roo.bootstrap.Button} btn
1032 * @param {Roo.EventObject} e
1033 * @param {boolean} pressed (also available as button.pressed)
1039 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1060 preventDefault: true,
1069 getAutoCreate : function(){
1077 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1078 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1079 this.tag = 'button';
1083 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1085 if (this.toggle == true) {
1088 cls: 'slider-frame roo-button',
1092 'data-on-text':'ON',
1093 'data-off-text':'OFF',
1094 cls: 'slider-button',
1099 // why are we validating the weights?
1100 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1101 cfg.cls += ' ' + this.weight;
1108 cfg.cls += ' close';
1110 cfg["aria-hidden"] = true;
1112 cfg.html = "×";
1118 if (this.theme==='default') {
1119 cfg.cls = 'btn roo-button';
1121 //if (this.parentType != 'Navbar') {
1122 this.weight = this.weight.length ? this.weight : 'default';
1124 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1126 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1127 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1128 cfg.cls += ' btn-' + outline + weight;
1129 if (this.weight == 'default') {
1131 cfg.cls += ' btn-' + this.weight;
1134 } else if (this.theme==='glow') {
1137 cfg.cls = 'btn-glow roo-button';
1139 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1141 cfg.cls += ' ' + this.weight;
1147 this.cls += ' inverse';
1151 if (this.active || this.pressed === true) {
1152 cfg.cls += ' active';
1155 if (this.disabled) {
1156 cfg.disabled = 'disabled';
1160 Roo.log('changing to ul' );
1162 this.glyphicon = 'caret';
1163 if (Roo.bootstrap.version == 4) {
1164 this.fa = 'caret-down';
1169 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1171 //gsRoo.log(this.parentType);
1172 if (this.parentType === 'Navbar' && !this.parent().bar) {
1173 Roo.log('changing to li?');
1182 href : this.href || '#'
1185 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1186 cfg.cls += ' dropdown';
1193 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1195 if (this.glyphicon) {
1196 cfg.html = ' ' + cfg.html;
1201 cls: 'glyphicon glyphicon-' + this.glyphicon
1206 cfg.html = ' ' + cfg.html;
1211 cls: 'fa fas fa-' + this.fa
1221 // cfg.cls='btn roo-button';
1225 var value = cfg.html;
1230 cls: 'glyphicon glyphicon-' + this.glyphicon,
1237 cls: 'fa fas fa-' + this.fa,
1242 var bw = this.badge_weight.length ? this.badge_weight :
1243 (this.weight.length ? this.weight : 'secondary');
1244 bw = bw == 'default' ? 'secondary' : bw;
1250 cls: 'badge badge-' + bw,
1259 cfg.cls += ' dropdown';
1260 cfg.html = typeof(cfg.html) != 'undefined' ?
1261 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1264 if (cfg.tag !== 'a' && this.href !== '') {
1265 throw "Tag must be a to set href.";
1266 } else if (this.href.length > 0) {
1267 cfg.href = this.href;
1270 if(this.removeClass){
1275 cfg.target = this.target;
1280 initEvents: function() {
1281 // Roo.log('init events?');
1282 // Roo.log(this.el.dom);
1285 if (typeof (this.menu) != 'undefined') {
1286 this.menu.parentType = this.xtype;
1287 this.menu.triggerEl = this.el;
1288 this.addxtype(Roo.apply({}, this.menu));
1292 if (this.el.hasClass('roo-button')) {
1293 this.el.on('click', this.onClick, this);
1294 this.el.on('dblclick', this.onDblClick, this);
1296 this.el.select('.roo-button').on('click', this.onClick, this);
1297 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1301 if(this.removeClass){
1302 this.el.on('click', this.onClick, this);
1305 if (this.group === true) {
1306 if (this.pressed === false || this.pressed === true) {
1309 this.pressed = false;
1310 this.setActive(this.pressed);
1315 this.el.enableDisplayMode();
1318 onClick : function(e)
1320 if (this.disabled) {
1324 Roo.log('button on click ');
1325 if(this.preventDefault){
1334 this.setActive(true);
1335 var pi = this.parent().items;
1336 for (var i = 0;i < pi.length;i++) {
1337 if (this == pi[i]) {
1340 if (pi[i].el.hasClass('roo-button')) {
1341 pi[i].setActive(false);
1344 this.fireEvent('click', this, e);
1348 if (this.pressed === true || this.pressed === false) {
1349 this.toggleActive(e);
1353 this.fireEvent('click', this, e);
1355 onDblClick: function(e)
1357 if (this.disabled) {
1360 if(this.preventDefault){
1363 this.fireEvent('dblclick', this, e);
1366 * Enables this button
1370 this.disabled = false;
1371 this.el.removeClass('disabled');
1372 this.el.dom.removeAttribute("disabled");
1376 * Disable this button
1378 disable : function()
1380 this.disabled = true;
1381 this.el.addClass('disabled');
1382 this.el.attr("disabled", "disabled")
1385 * sets the active state on/off,
1386 * @param {Boolean} state (optional) Force a particular state
1388 setActive : function(v) {
1390 this.el[v ? 'addClass' : 'removeClass']('active');
1394 * toggles the current active state
1396 toggleActive : function(e)
1398 this.setActive(!this.pressed); // this modifies pressed...
1399 this.fireEvent('toggle', this, e, this.pressed);
1402 * get the current active state
1403 * @return {boolean} true if it's active
1405 isActive : function()
1407 return this.el.hasClass('active');
1410 * set the text of the first selected button
1412 setText : function(str)
1414 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1417 * get the text of the first selected button
1419 getText : function()
1421 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1424 setWeight : function(str)
1426 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1427 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1429 var outline = this.outline ? 'outline-' : '';
1430 if (str == 'default') {
1431 this.el.addClass('btn-default btn-outline-secondary');
1434 this.el.addClass('btn-' + outline + str);
1439 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1441 Roo.bootstrap.Button.weights = [
1461 * @class Roo.bootstrap.Column
1462 * @extends Roo.bootstrap.Component
1463 * Bootstrap Column class
1464 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1465 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1466 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1467 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1468 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1469 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1470 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1471 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1474 * @cfg {Boolean} hidden (true|false) hide the element
1475 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1476 * @cfg {String} fa (ban|check|...) font awesome icon
1477 * @cfg {Number} fasize (1|2|....) font awsome size
1479 * @cfg {String} icon (info-sign|check|...) glyphicon name
1481 * @cfg {String} html content of column.
1484 * Create a new Column
1485 * @param {Object} config The config object
1488 Roo.bootstrap.Column = function(config){
1489 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1492 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1510 getAutoCreate : function(){
1511 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1519 var sizes = ['xs','sm','md','lg'];
1520 sizes.map(function(size ,ix){
1521 //Roo.log( size + ':' + settings[size]);
1523 if (settings[size+'off'] !== false) {
1524 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1527 if (settings[size] === false) {
1531 if (!settings[size]) { // 0 = hidden
1532 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1534 for (var i = ix; i > -1; i--) {
1535 cfg.cls += ' d-' + sizes[i] + '-none';
1541 cfg.cls += ' col-' + size + '-' + settings[size] + (
1542 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548 cfg.cls += ' hidden';
1551 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1552 cfg.cls +=' alert alert-' + this.alert;
1556 if (this.html.length) {
1557 cfg.html = this.html;
1561 if (this.fasize > 1) {
1562 fasize = ' fa-' + this.fasize + 'x';
1564 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1569 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1588 * @class Roo.bootstrap.Container
1589 * @extends Roo.bootstrap.Component
1590 * Bootstrap Container class
1591 * @cfg {Boolean} jumbotron is it a jumbotron element
1592 * @cfg {String} html content of element
1593 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1594 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1595 * @cfg {String} header content of header (for panel)
1596 * @cfg {String} footer content of footer (for panel)
1597 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1598 * @cfg {String} tag (header|aside|section) type of HTML tag.
1599 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1600 * @cfg {String} fa font awesome icon
1601 * @cfg {String} icon (info-sign|check|...) glyphicon name
1602 * @cfg {Boolean} hidden (true|false) hide the element
1603 * @cfg {Boolean} expandable (true|false) default false
1604 * @cfg {Boolean} expanded (true|false) default true
1605 * @cfg {String} rheader contet on the right of header
1606 * @cfg {Boolean} clickable (true|false) default false
1610 * Create a new Container
1611 * @param {Object} config The config object
1614 Roo.bootstrap.Container = function(config){
1615 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621 * After the panel has been expand
1623 * @param {Roo.bootstrap.Container} this
1628 * After the panel has been collapsed
1630 * @param {Roo.bootstrap.Container} this
1635 * When a element is chick
1636 * @param {Roo.bootstrap.Container} this
1637 * @param {Roo.EventObject} e
1643 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1661 getChildContainer : function() {
1667 if (this.panel.length) {
1668 return this.el.select('.panel-body',true).first();
1675 getAutoCreate : function(){
1678 tag : this.tag || 'div',
1682 if (this.jumbotron) {
1683 cfg.cls = 'jumbotron';
1688 // - this is applied by the parent..
1690 // cfg.cls = this.cls + '';
1693 if (this.sticky.length) {
1695 var bd = Roo.get(document.body);
1696 if (!bd.hasClass('bootstrap-sticky')) {
1697 bd.addClass('bootstrap-sticky');
1698 Roo.select('html',true).setStyle('height', '100%');
1701 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1705 if (this.well.length) {
1706 switch (this.well) {
1709 cfg.cls +=' well well-' +this.well;
1718 cfg.cls += ' hidden';
1722 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1723 cfg.cls +=' alert alert-' + this.alert;
1728 if (this.panel.length) {
1729 cfg.cls += ' panel panel-' + this.panel;
1731 if (this.header.length) {
1735 if(this.expandable){
1737 cfg.cls = cfg.cls + ' expandable';
1741 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1749 cls : 'panel-title',
1750 html : (this.expandable ? ' ' : '') + this.header
1754 cls: 'panel-header-right',
1760 cls : 'panel-heading',
1761 style : this.expandable ? 'cursor: pointer' : '',
1769 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1774 if (this.footer.length) {
1776 cls : 'panel-footer',
1785 body.html = this.html || cfg.html;
1786 // prefix with the icons..
1788 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1791 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1796 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1797 cfg.cls = 'container';
1803 initEvents: function()
1805 if(this.expandable){
1806 var headerEl = this.headerEl();
1809 headerEl.on('click', this.onToggleClick, this);
1814 this.el.on('click', this.onClick, this);
1819 onToggleClick : function()
1821 var headerEl = this.headerEl();
1837 if(this.fireEvent('expand', this)) {
1839 this.expanded = true;
1841 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1843 this.el.select('.panel-body',true).first().removeClass('hide');
1845 var toggleEl = this.toggleEl();
1851 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1856 collapse : function()
1858 if(this.fireEvent('collapse', this)) {
1860 this.expanded = false;
1862 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1863 this.el.select('.panel-body',true).first().addClass('hide');
1865 var toggleEl = this.toggleEl();
1871 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1875 toggleEl : function()
1877 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1881 return this.el.select('.panel-heading .fa',true).first();
1884 headerEl : function()
1886 if(!this.el || !this.panel.length || !this.header.length){
1890 return this.el.select('.panel-heading',true).first()
1895 if(!this.el || !this.panel.length){
1899 return this.el.select('.panel-body',true).first()
1902 titleEl : function()
1904 if(!this.el || !this.panel.length || !this.header.length){
1908 return this.el.select('.panel-title',true).first();
1911 setTitle : function(v)
1913 var titleEl = this.titleEl();
1919 titleEl.dom.innerHTML = v;
1922 getTitle : function()
1925 var titleEl = this.titleEl();
1931 return titleEl.dom.innerHTML;
1934 setRightTitle : function(v)
1936 var t = this.el.select('.panel-header-right',true).first();
1942 t.dom.innerHTML = v;
1945 onClick : function(e)
1949 this.fireEvent('click', this, e);
1956 * This is BS4's Card element.. - similar to our containers probably..
1960 * @class Roo.bootstrap.Card
1961 * @extends Roo.bootstrap.Component
1962 * Bootstrap Card class
1965 * possible... may not be implemented..
1966 * @cfg {String} header_image src url of image.
1967 * @cfg {String|Object} header
1968 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1969 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1971 * @cfg {String} title
1972 * @cfg {String} subtitle
1973 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1974 * @cfg {String} footer
1976 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1978 * @cfg {String} margin (0|1|2|3|4|5|auto)
1979 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1980 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1981 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1982 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1983 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1986 * @cfg {String} padding (0|1|2|3|4|5)
1987 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1988 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1989 * @cfg {String} padding_left (0|1|2|3|4|5)
1990 * @cfg {String} padding_right (0|1|2|3|4|5)
1991 * @cfg {String} padding_x (0|1|2|3|4|5)
1992 * @cfg {String} padding_y (0|1|2|3|4|5)
1994 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1995 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1996 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1997 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1998 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @config {Boolean} dragable if this card can be dragged.
2001 * @config {String} drag_group group for drag
2002 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2003 * @config {String} drop_group group for drag
2005 * @config {Boolean} collapsable can the body be collapsed.
2006 * @config {Boolean} collapsed is the body collapsed when rendered...
2007 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2008 * @config {Boolean} rotated is the body rotated when rendered...
2011 * Create a new Container
2012 * @param {Object} config The config object
2015 Roo.bootstrap.Card = function(config){
2016 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022 * When a element a card is dropped
2023 * @param {Roo.bootstrap.Card} this
2026 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2027 * @param {String} position 'above' or 'below'
2028 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034 * When a element a card is rotate
2035 * @param {Roo.bootstrap.Card} this
2036 * @param {Roo.Element} n the node being dropped?
2037 * @param {Boolean} rotate status
2042 * When a card element is dragged over ready to drop (return false to block dropable)
2043 * @param {Roo.bootstrap.Card} this
2044 * @param {Object} data from dragdrop
2052 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2057 margin: '', /// may be better in component?
2087 collapsable : false,
2096 childContainer : false,
2097 dropEl : false, /// the dom placeholde element that indicates drop location.
2098 containerEl: false, // body container
2099 bodyEl: false, // card-body
2100 headerContainerEl : false, //
2102 header_imageEl : false,
2105 layoutCls : function()
2109 Roo.log(this.margin_bottom.length);
2110 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2111 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2113 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2114 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2116 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2117 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2121 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2122 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2123 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2127 // more generic support?
2135 // Roo.log("Call onRender: " + this.xtype);
2136 /* We are looking at something like this.
2138 <img src="..." class="card-img-top" alt="...">
2139 <div class="card-body">
2140 <h5 class="card-title">Card title</h5>
2141 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2143 >> this bit is really the body...
2144 <div> << we will ad dthis in hopefully it will not break shit.
2146 ** card text does not actually have any styling...
2148 <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>
2151 <a href="#" class="card-link">Card link</a>
2154 <div class="card-footer">
2155 <small class="text-muted">Last updated 3 mins ago</small>
2159 getAutoCreate : function(){
2167 if (this.weight.length && this.weight != 'light') {
2168 cfg.cls += ' text-white';
2170 cfg.cls += ' text-dark'; // need as it's nested..
2172 if (this.weight.length) {
2173 cfg.cls += ' bg-' + this.weight;
2176 cfg.cls += ' ' + this.layoutCls();
2179 var hdr_ctr = false;
2180 if (this.header.length) {
2182 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2183 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2191 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197 if (this.collapsable) {
2200 cls : 'd-block user-select-none',
2204 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2209 hdr.cn.push(hdr_ctr);
2214 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2219 if (this.header_image.length) {
2222 cls : 'card-img-top',
2223 src: this.header_image // escape?
2228 cls : 'card-img-top d-none'
2234 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2238 if (this.collapsable || this.rotateable) {
2241 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2248 if (this.title.length) {
2252 src: this.title // escape?
2256 if (this.subtitle.length) {
2260 src: this.subtitle // escape?
2266 cls : 'roo-card-body-ctr'
2269 if (this.html.length) {
2275 // fixme ? handle objects?
2277 if (this.footer.length) {
2280 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2285 cfg.cn.push({cls : 'card-footer d-none'});
2294 getCardHeader : function()
2296 var ret = this.el.select('.card-header',true).first();
2297 if (ret.hasClass('d-none')) {
2298 ret.removeClass('d-none');
2303 getCardFooter : function()
2305 var ret = this.el.select('.card-footer',true).first();
2306 if (ret.hasClass('d-none')) {
2307 ret.removeClass('d-none');
2312 getCardImageTop : function()
2314 var ret = this.header_imageEl;
2315 if (ret.hasClass('d-none')) {
2316 ret.removeClass('d-none');
2322 getChildContainer : function()
2328 return this.el.select('.roo-card-body-ctr',true).first();
2331 initEvents: function()
2333 this.bodyEl = this.el.select('.card-body',true).first();
2334 this.containerEl = this.getChildContainer();
2336 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2337 containerScroll: true,
2338 ddGroup: this.drag_group || 'default_card_drag_group'
2340 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2342 if (this.dropable) {
2343 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2344 containerScroll: true,
2345 ddGroup: this.drop_group || 'default_card_drag_group'
2347 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2348 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2349 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2350 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2351 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2354 if (this.collapsable) {
2355 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2357 if (this.rotateable) {
2358 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2360 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2362 this.footerEl = this.el.select('.card-footer',true).first();
2363 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2364 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2365 this.headerEl = this.el.select('.card-header',true).first();
2368 this.el.addClass('roo-card-rotated');
2369 this.fireEvent('rotate', this, true);
2371 this.header_imageEl = this.el.select('.card-img-top',true).first();
2372 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2375 getDragData : function(e)
2377 var target = this.getEl();
2379 //this.handleSelection(e);
2384 nodes: this.getEl(),
2389 dragData.ddel = target.dom ; // the div element
2390 Roo.log(target.getWidth( ));
2391 dragData.ddel.style.width = target.getWidth() + 'px';
2398 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2399 * whole Element becomes the target, and this causes the drop gesture to append.
2401 * Returns an object:
2404 position : 'below' or 'above'
2405 card : relateive to card OBJECT (or true for no cards listed)
2406 items_n : relative to nth item in list
2407 card_n : relative to nth card in list
2412 getTargetFromEvent : function(e, dragged_card_el)
2414 var target = e.getTarget();
2415 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2416 target = target.parentNode;
2427 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2428 // see if target is one of the 'cards'...
2431 //Roo.log(this.items.length);
2434 var last_card_n = 0;
2436 for (var i = 0;i< this.items.length;i++) {
2438 if (!this.items[i].el.hasClass('card')) {
2441 pos = this.getDropPoint(e, this.items[i].el.dom);
2443 cards_len = ret.cards.length;
2444 //Roo.log(this.items[i].el.dom.id);
2445 ret.cards.push(this.items[i]);
2447 if (ret.card_n < 0 && pos == 'above') {
2448 ret.position = cards_len > 0 ? 'below' : pos;
2449 ret.items_n = i > 0 ? i - 1 : 0;
2450 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2451 ret.card = ret.cards[ret.card_n];
2454 if (!ret.cards.length) {
2456 ret.position = 'below';
2460 // could not find a card.. stick it at the end..
2461 if (ret.card_n < 0) {
2462 ret.card_n = last_card_n;
2463 ret.card = ret.cards[last_card_n];
2464 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2465 ret.position = 'below';
2468 if (this.items[ret.items_n].el == dragged_card_el) {
2472 if (ret.position == 'below') {
2473 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2475 if (card_after && card_after.el == dragged_card_el) {
2482 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2484 if (card_before && card_before.el == dragged_card_el) {
2491 onNodeEnter : function(n, dd, e, data){
2494 onNodeOver : function(n, dd, e, data)
2497 var target_info = this.getTargetFromEvent(e,data.source.el);
2498 if (target_info === false) {
2499 this.dropPlaceHolder('hide');
2502 Roo.log(['getTargetFromEvent', target_info ]);
2505 if (this.fireEvent('cardover', this, [ data ]) === false) {
2509 this.dropPlaceHolder('show', target_info,data);
2513 onNodeOut : function(n, dd, e, data){
2514 this.dropPlaceHolder('hide');
2517 onNodeDrop : function(n, dd, e, data)
2520 // call drop - return false if
2522 // this could actually fail - if the Network drops..
2523 // we will ignore this at present..- client should probably reload
2524 // the whole set of cards if stuff like that fails.
2527 var info = this.getTargetFromEvent(e,data.source.el);
2528 if (info === false) {
2531 this.dropPlaceHolder('hide');
2535 this.acceptCard(data.source, info.position, info.card, info.items_n);
2539 firstChildCard : function()
2541 for (var i = 0;i< this.items.length;i++) {
2543 if (!this.items[i].el.hasClass('card')) {
2546 return this.items[i];
2548 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2553 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2555 acceptCard : function(move_card, position, next_to_card )
2557 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2561 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2563 move_card.parent().removeCard(move_card);
2566 var dom = move_card.el.dom;
2567 dom.style.width = ''; // clear with - which is set by drag.
2569 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2570 var cardel = next_to_card.el.dom;
2572 if (position == 'above' ) {
2573 cardel.parentNode.insertBefore(dom, cardel);
2574 } else if (cardel.nextSibling) {
2575 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2577 cardel.parentNode.append(dom);
2580 // card container???
2581 this.containerEl.dom.append(dom);
2584 //FIXME HANDLE card = true
2586 // add this to the correct place in items.
2588 // remove Card from items.
2591 if (this.items.length) {
2593 //Roo.log([info.items_n, info.position, this.items.length]);
2594 for (var i =0; i < this.items.length; i++) {
2595 if (i == to_items_n && position == 'above') {
2596 nitems.push(move_card);
2598 nitems.push(this.items[i]);
2599 if (i == to_items_n && position == 'below') {
2600 nitems.push(move_card);
2603 this.items = nitems;
2604 Roo.log(this.items);
2606 this.items.push(move_card);
2609 move_card.parentId = this.id;
2615 removeCard : function(c)
2617 this.items = this.items.filter(function(e) { return e != c });
2620 dom.parentNode.removeChild(dom);
2621 dom.style.width = ''; // clear with - which is set by drag.
2626 /** Decide whether to drop above or below a View node. */
2627 getDropPoint : function(e, n, dd)
2632 if (n == this.containerEl.dom) {
2635 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2636 var c = t + (b - t) / 2;
2637 var y = Roo.lib.Event.getPageY(e);
2644 onToggleCollapse : function(e)
2646 if (this.collapsed) {
2647 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2648 this.collapsableEl.addClass('show');
2649 this.collapsed = false;
2652 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2653 this.collapsableEl.removeClass('show');
2654 this.collapsed = true;
2659 onToggleRotate : function(e)
2661 this.collapsableEl.removeClass('show');
2662 this.footerEl.removeClass('d-none');
2663 this.el.removeClass('roo-card-rotated');
2664 this.el.removeClass('d-none');
2667 this.collapsableEl.addClass('show');
2668 this.rotated = false;
2669 this.fireEvent('rotate', this, this.rotated);
2672 this.el.addClass('roo-card-rotated');
2673 this.footerEl.addClass('d-none');
2674 this.el.select('.roo-collapsable').removeClass('show');
2676 this.rotated = true;
2677 this.fireEvent('rotate', this, this.rotated);
2681 dropPlaceHolder: function (action, info, data)
2683 if (this.dropEl === false) {
2684 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2688 this.dropEl.removeClass(['d-none', 'd-block']);
2689 if (action == 'hide') {
2691 this.dropEl.addClass('d-none');
2694 // FIXME - info.card == true!!!
2695 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2697 if (info.card !== true) {
2698 var cardel = info.card.el.dom;
2700 if (info.position == 'above') {
2701 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2702 } else if (cardel.nextSibling) {
2703 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2705 cardel.parentNode.append(this.dropEl.dom);
2708 // card container???
2709 this.containerEl.dom.append(this.dropEl.dom);
2712 this.dropEl.addClass('d-block roo-card-dropzone');
2714 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2721 setHeaderText: function(html)
2724 if (this.headerContainerEl) {
2725 this.headerContainerEl.dom.innerHTML = html;
2728 onHeaderImageLoad : function(ev, he)
2730 if (!this.header_image_fit_square) {
2734 var hw = he.naturalHeight / he.naturalWidth;
2737 //var w = he.dom.naturalWidth;
2740 he.style.position = 'relative';
2742 var nw = (ww * (1/hw));
2743 Roo.get(he).setSize( ww * (1/hw), ww);
2744 he.style.left = ((ww - nw)/ 2) + 'px';
2745 he.style.position = 'relative';
2756 * Card header - holder for the card header elements.
2761 * @class Roo.bootstrap.CardHeader
2762 * @extends Roo.bootstrap.Element
2763 * Bootstrap CardHeader class
2765 * Create a new Card Header - that you can embed children into
2766 * @param {Object} config The config object
2769 Roo.bootstrap.CardHeader = function(config){
2770 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2773 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2776 container_method : 'getCardHeader'
2789 * Card footer - holder for the card footer elements.
2794 * @class Roo.bootstrap.CardFooter
2795 * @extends Roo.bootstrap.Element
2796 * Bootstrap CardFooter class
2798 * Create a new Card Footer - that you can embed children into
2799 * @param {Object} config The config object
2802 Roo.bootstrap.CardFooter = function(config){
2803 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2806 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2809 container_method : 'getCardFooter'
2822 * Card header - holder for the card header elements.
2827 * @class Roo.bootstrap.CardImageTop
2828 * @extends Roo.bootstrap.Element
2829 * Bootstrap CardImageTop class
2831 * Create a new Card Image Top container
2832 * @param {Object} config The config object
2835 Roo.bootstrap.CardImageTop = function(config){
2836 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2839 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2842 container_method : 'getCardImageTop'
2860 * @class Roo.bootstrap.Img
2861 * @extends Roo.bootstrap.Component
2862 * Bootstrap Img class
2863 * @cfg {Boolean} imgResponsive false | true
2864 * @cfg {String} border rounded | circle | thumbnail
2865 * @cfg {String} src image source
2866 * @cfg {String} alt image alternative text
2867 * @cfg {String} href a tag href
2868 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2869 * @cfg {String} xsUrl xs image source
2870 * @cfg {String} smUrl sm image source
2871 * @cfg {String} mdUrl md image source
2872 * @cfg {String} lgUrl lg image source
2875 * Create a new Input
2876 * @param {Object} config The config object
2879 Roo.bootstrap.Img = function(config){
2880 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2886 * The img click event for the img.
2887 * @param {Roo.EventObject} e
2893 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2895 imgResponsive: true,
2905 getAutoCreate : function()
2907 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2908 return this.createSingleImg();
2913 cls: 'roo-image-responsive-group',
2918 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2920 if(!_this[size + 'Url']){
2926 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2927 html: _this.html || cfg.html,
2928 src: _this[size + 'Url']
2931 img.cls += ' roo-image-responsive-' + size;
2933 var s = ['xs', 'sm', 'md', 'lg'];
2935 s.splice(s.indexOf(size), 1);
2937 Roo.each(s, function(ss){
2938 img.cls += ' hidden-' + ss;
2941 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2942 cfg.cls += ' img-' + _this.border;
2946 cfg.alt = _this.alt;
2959 a.target = _this.target;
2963 cfg.cn.push((_this.href) ? a : img);
2970 createSingleImg : function()
2974 cls: (this.imgResponsive) ? 'img-responsive' : '',
2976 src : 'about:blank' // just incase src get's set to undefined?!?
2979 cfg.html = this.html || cfg.html;
2981 cfg.src = this.src || cfg.src;
2983 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2984 cfg.cls += ' img-' + this.border;
3001 a.target = this.target;
3006 return (this.href) ? a : cfg;
3009 initEvents: function()
3012 this.el.on('click', this.onClick, this);
3017 onClick : function(e)
3019 Roo.log('img onclick');
3020 this.fireEvent('click', this, e);
3023 * Sets the url of the image - used to update it
3024 * @param {String} url the url of the image
3027 setSrc : function(url)
3031 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3032 this.el.dom.src = url;
3036 this.el.select('img', true).first().dom.src = url;
3052 * @class Roo.bootstrap.Link
3053 * @extends Roo.bootstrap.Component
3054 * Bootstrap Link Class
3055 * @cfg {String} alt image alternative text
3056 * @cfg {String} href a tag href
3057 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3058 * @cfg {String} html the content of the link.
3059 * @cfg {String} anchor name for the anchor link
3060 * @cfg {String} fa - favicon
3062 * @cfg {Boolean} preventDefault (true | false) default false
3066 * Create a new Input
3067 * @param {Object} config The config object
3070 Roo.bootstrap.Link = function(config){
3071 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3077 * The img click event for the img.
3078 * @param {Roo.EventObject} e
3084 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3088 preventDefault: false,
3094 getAutoCreate : function()
3096 var html = this.html || '';
3098 if (this.fa !== false) {
3099 html = '<i class="fa fa-' + this.fa + '"></i>';
3104 // anchor's do not require html/href...
3105 if (this.anchor === false) {
3107 cfg.href = this.href || '#';
3109 cfg.name = this.anchor;
3110 if (this.html !== false || this.fa !== false) {
3113 if (this.href !== false) {
3114 cfg.href = this.href;
3118 if(this.alt !== false){
3123 if(this.target !== false) {
3124 cfg.target = this.target;
3130 initEvents: function() {
3132 if(!this.href || this.preventDefault){
3133 this.el.on('click', this.onClick, this);
3137 onClick : function(e)
3139 if(this.preventDefault){
3142 //Roo.log('img onclick');
3143 this.fireEvent('click', this, e);
3156 * @class Roo.bootstrap.Header
3157 * @extends Roo.bootstrap.Component
3158 * Bootstrap Header class
3159 * @cfg {String} html content of header
3160 * @cfg {Number} level (1|2|3|4|5|6) default 1
3163 * Create a new Header
3164 * @param {Object} config The config object
3168 Roo.bootstrap.Header = function(config){
3169 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3172 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3180 getAutoCreate : function(){
3185 tag: 'h' + (1 *this.level),
3186 html: this.html || ''
3198 * Ext JS Library 1.1.1
3199 * Copyright(c) 2006-2007, Ext JS, LLC.
3201 * Originally Released Under LGPL - original licence link has changed is not relivant.
3204 * <script type="text/javascript">
3208 * @class Roo.bootstrap.MenuMgr
3209 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3212 Roo.bootstrap.MenuMgr = function(){
3213 var menus, active, groups = {}, attached = false, lastShow = new Date();
3215 // private - called when first menu is created
3218 active = new Roo.util.MixedCollection();
3219 Roo.get(document).addKeyListener(27, function(){
3220 if(active.length > 0){
3228 if(active && active.length > 0){
3229 var c = active.clone();
3239 if(active.length < 1){
3240 Roo.get(document).un("mouseup", onMouseDown);
3248 var last = active.last();
3249 lastShow = new Date();
3252 Roo.get(document).on("mouseup", onMouseDown);
3257 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3258 m.parentMenu.activeChild = m;
3259 }else if(last && last.isVisible()){
3260 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3265 function onBeforeHide(m){
3267 m.activeChild.hide();
3269 if(m.autoHideTimer){
3270 clearTimeout(m.autoHideTimer);
3271 delete m.autoHideTimer;
3276 function onBeforeShow(m){
3277 var pm = m.parentMenu;
3278 if(!pm && !m.allowOtherMenus){
3280 }else if(pm && pm.activeChild && active != m){
3281 pm.activeChild.hide();
3285 // private this should really trigger on mouseup..
3286 function onMouseDown(e){
3287 Roo.log("on Mouse Up");
3289 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3290 Roo.log("MenuManager hideAll");
3299 function onBeforeCheck(mi, state){
3301 var g = groups[mi.group];
3302 for(var i = 0, l = g.length; i < l; i++){
3304 g[i].setChecked(false);
3313 * Hides all menus that are currently visible
3315 hideAll : function(){
3320 register : function(menu){
3324 menus[menu.id] = menu;
3325 menu.on("beforehide", onBeforeHide);
3326 menu.on("hide", onHide);
3327 menu.on("beforeshow", onBeforeShow);
3328 menu.on("show", onShow);
3330 if(g && menu.events["checkchange"]){
3334 groups[g].push(menu);
3335 menu.on("checkchange", onCheck);
3340 * Returns a {@link Roo.menu.Menu} object
3341 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3342 * be used to generate and return a new Menu instance.
3344 get : function(menu){
3345 if(typeof menu == "string"){ // menu id
3347 }else if(menu.events){ // menu instance
3350 /*else if(typeof menu.length == 'number'){ // array of menu items?
3351 return new Roo.bootstrap.Menu({items:menu});
3352 }else{ // otherwise, must be a config
3353 return new Roo.bootstrap.Menu(menu);
3360 unregister : function(menu){
3361 delete menus[menu.id];
3362 menu.un("beforehide", onBeforeHide);
3363 menu.un("hide", onHide);
3364 menu.un("beforeshow", onBeforeShow);
3365 menu.un("show", onShow);
3367 if(g && menu.events["checkchange"]){
3368 groups[g].remove(menu);
3369 menu.un("checkchange", onCheck);
3374 registerCheckable : function(menuItem){
3375 var g = menuItem.group;
3380 groups[g].push(menuItem);
3381 menuItem.on("beforecheckchange", onBeforeCheck);
3386 unregisterCheckable : function(menuItem){
3387 var g = menuItem.group;
3389 groups[g].remove(menuItem);
3390 menuItem.un("beforecheckchange", onBeforeCheck);
3402 * @class Roo.bootstrap.Menu
3403 * @extends Roo.bootstrap.Component
3404 * Bootstrap Menu class - container for MenuItems
3405 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3406 * @cfg {bool} hidden if the menu should be hidden when rendered.
3407 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3408 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3412 * @param {Object} config The config object
3416 Roo.bootstrap.Menu = function(config){
3417 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3418 if (this.registerMenu && this.type != 'treeview') {
3419 Roo.bootstrap.MenuMgr.register(this);
3426 * Fires before this menu is displayed (return false to block)
3427 * @param {Roo.menu.Menu} this
3432 * Fires before this menu is hidden (return false to block)
3433 * @param {Roo.menu.Menu} this
3438 * Fires after this menu is displayed
3439 * @param {Roo.menu.Menu} this
3444 * Fires after this menu is hidden
3445 * @param {Roo.menu.Menu} this
3450 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3451 * @param {Roo.menu.Menu} this
3452 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3453 * @param {Roo.EventObject} e
3458 * Fires when the mouse is hovering over this menu
3459 * @param {Roo.menu.Menu} this
3460 * @param {Roo.EventObject} e
3461 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3466 * Fires when the mouse exits this menu
3467 * @param {Roo.menu.Menu} this
3468 * @param {Roo.EventObject} e
3469 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3474 * Fires when a menu item contained in this menu is clicked
3475 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3476 * @param {Roo.EventObject} e
3480 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3483 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3487 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3490 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3492 registerMenu : true,
3494 menuItems :false, // stores the menu items..
3504 getChildContainer : function() {
3508 getAutoCreate : function(){
3510 //if (['right'].indexOf(this.align)!==-1) {
3511 // cfg.cn[1].cls += ' pull-right'
3517 cls : 'dropdown-menu' ,
3518 style : 'z-index:1000'
3522 if (this.type === 'submenu') {
3523 cfg.cls = 'submenu active';
3525 if (this.type === 'treeview') {
3526 cfg.cls = 'treeview-menu';
3531 initEvents : function() {
3533 // Roo.log("ADD event");
3534 // Roo.log(this.triggerEl.dom);
3536 this.triggerEl.on('click', this.onTriggerClick, this);
3538 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3541 if (this.triggerEl.hasClass('nav-item')) {
3542 // dropdown toggle on the 'a' in BS4?
3543 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3545 this.triggerEl.addClass('dropdown-toggle');
3548 this.el.on('touchstart' , this.onTouch, this);
3550 this.el.on('click' , this.onClick, this);
3552 this.el.on("mouseover", this.onMouseOver, this);
3553 this.el.on("mouseout", this.onMouseOut, this);
3557 findTargetItem : function(e)
3559 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3563 //Roo.log(t); Roo.log(t.id);
3565 //Roo.log(this.menuitems);
3566 return this.menuitems.get(t.id);
3568 //return this.items.get(t.menuItemId);
3574 onTouch : function(e)
3576 Roo.log("menu.onTouch");
3577 //e.stopEvent(); this make the user popdown broken
3581 onClick : function(e)
3583 Roo.log("menu.onClick");
3585 var t = this.findTargetItem(e);
3586 if(!t || t.isContainer){
3591 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3592 if(t == this.activeItem && t.shouldDeactivate(e)){
3593 this.activeItem.deactivate();
3594 delete this.activeItem;
3598 this.setActiveItem(t, true);
3606 Roo.log('pass click event');
3610 this.fireEvent("click", this, t, e);
3614 if(!t.href.length || t.href == '#'){
3615 (function() { _this.hide(); }).defer(100);
3620 onMouseOver : function(e){
3621 var t = this.findTargetItem(e);
3624 // if(t.canActivate && !t.disabled){
3625 // this.setActiveItem(t, true);
3629 this.fireEvent("mouseover", this, e, t);
3631 isVisible : function(){
3632 return !this.hidden;
3634 onMouseOut : function(e){
3635 var t = this.findTargetItem(e);
3638 // if(t == this.activeItem && t.shouldDeactivate(e)){
3639 // this.activeItem.deactivate();
3640 // delete this.activeItem;
3643 this.fireEvent("mouseout", this, e, t);
3648 * Displays this menu relative to another element
3649 * @param {String/HTMLElement/Roo.Element} element The element to align to
3650 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3651 * the element (defaults to this.defaultAlign)
3652 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3654 show : function(el, pos, parentMenu)
3656 if (false === this.fireEvent("beforeshow", this)) {
3657 Roo.log("show canceled");
3660 this.parentMenu = parentMenu;
3665 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3668 * Displays this menu at a specific xy position
3669 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3670 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3672 showAt : function(xy, parentMenu, /* private: */_e){
3673 this.parentMenu = parentMenu;
3678 this.fireEvent("beforeshow", this);
3679 //xy = this.el.adjustForConstraints(xy);
3683 this.hideMenuItems();
3684 this.hidden = false;
3685 this.triggerEl.addClass('open');
3686 this.el.addClass('show');
3688 // reassign x when hitting right
3689 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3690 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3693 // reassign y when hitting bottom
3694 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3695 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3698 // but the list may align on trigger left or trigger top... should it be a properity?
3700 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3705 this.fireEvent("show", this);
3711 this.doFocus.defer(50, this);
3715 doFocus : function(){
3717 this.focusEl.focus();
3722 * Hides this menu and optionally all parent menus
3723 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3725 hide : function(deep)
3727 if (false === this.fireEvent("beforehide", this)) {
3728 Roo.log("hide canceled");
3731 this.hideMenuItems();
3732 if(this.el && this.isVisible()){
3734 if(this.activeItem){
3735 this.activeItem.deactivate();
3736 this.activeItem = null;
3738 this.triggerEl.removeClass('open');;
3739 this.el.removeClass('show');
3741 this.fireEvent("hide", this);
3743 if(deep === true && this.parentMenu){
3744 this.parentMenu.hide(true);
3748 onTriggerClick : function(e)
3750 Roo.log('trigger click');
3752 var target = e.getTarget();
3754 Roo.log(target.nodeName.toLowerCase());
3756 if(target.nodeName.toLowerCase() === 'i'){
3762 onTriggerPress : function(e)
3764 Roo.log('trigger press');
3765 //Roo.log(e.getTarget());
3766 // Roo.log(this.triggerEl.dom);
3768 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3769 var pel = Roo.get(e.getTarget());
3770 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3771 Roo.log('is treeview or dropdown?');
3775 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3779 if (this.isVisible()) {
3784 this.show(this.triggerEl, '?', false);
3787 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3794 hideMenuItems : function()
3796 Roo.log("hide Menu Items");
3801 this.el.select('.open',true).each(function(aa) {
3803 aa.removeClass('open');
3807 addxtypeChild : function (tree, cntr) {
3808 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3810 this.menuitems.add(comp);
3822 this.getEl().dom.innerHTML = '';
3823 this.menuitems.clear();
3837 * @class Roo.bootstrap.MenuItem
3838 * @extends Roo.bootstrap.Component
3839 * Bootstrap MenuItem class
3840 * @cfg {String} html the menu label
3841 * @cfg {String} href the link
3842 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3843 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3844 * @cfg {Boolean} active used on sidebars to highlight active itesm
3845 * @cfg {String} fa favicon to show on left of menu item.
3846 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3850 * Create a new MenuItem
3851 * @param {Object} config The config object
3855 Roo.bootstrap.MenuItem = function(config){
3856 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3861 * The raw click event for the entire grid.
3862 * @param {Roo.bootstrap.MenuItem} this
3863 * @param {Roo.EventObject} e
3869 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3873 preventDefault: false,
3874 isContainer : false,
3878 getAutoCreate : function(){
3880 if(this.isContainer){
3883 cls: 'dropdown-menu-item '
3893 cls : 'dropdown-item',
3898 if (this.fa !== false) {
3901 cls : 'fa fa-' + this.fa
3910 cls: 'dropdown-menu-item',
3913 if (this.parent().type == 'treeview') {
3914 cfg.cls = 'treeview-menu';
3917 cfg.cls += ' active';
3922 anc.href = this.href || cfg.cn[0].href ;
3923 ctag.html = this.html || cfg.cn[0].html ;
3927 initEvents: function()
3929 if (this.parent().type == 'treeview') {
3930 this.el.select('a').on('click', this.onClick, this);
3934 this.menu.parentType = this.xtype;
3935 this.menu.triggerEl = this.el;
3936 this.menu = this.addxtype(Roo.apply({}, this.menu));
3940 onClick : function(e)
3942 Roo.log('item on click ');
3944 if(this.preventDefault){
3947 //this.parent().hideMenuItems();
3949 this.fireEvent('click', this, e);
3968 * @class Roo.bootstrap.MenuSeparator
3969 * @extends Roo.bootstrap.Component
3970 * Bootstrap MenuSeparator class
3973 * Create a new MenuItem
3974 * @param {Object} config The config object
3978 Roo.bootstrap.MenuSeparator = function(config){
3979 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3982 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3984 getAutoCreate : function(){
4003 * @class Roo.bootstrap.Modal
4004 * @extends Roo.bootstrap.Component
4005 * Bootstrap Modal class
4006 * @cfg {String} title Title of dialog
4007 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4008 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4009 * @cfg {Boolean} specificTitle default false
4010 * @cfg {Array} buttons Array of buttons or standard button set..
4011 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4012 * @cfg {Boolean} animate default true
4013 * @cfg {Boolean} allow_close default true
4014 * @cfg {Boolean} fitwindow default false
4015 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4016 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4017 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4018 * @cfg {String} size (sm|lg|xl) default empty
4019 * @cfg {Number} max_width set the max width of modal
4020 * @cfg {Boolean} editableTitle can the title be edited
4025 * Create a new Modal Dialog
4026 * @param {Object} config The config object
4029 Roo.bootstrap.Modal = function(config){
4030 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4035 * The raw btnclick event for the button
4036 * @param {Roo.EventObject} e
4041 * Fire when dialog resize
4042 * @param {Roo.bootstrap.Modal} this
4043 * @param {Roo.EventObject} e
4047 * @event titlechanged
4048 * Fire when the editable title has been changed
4049 * @param {Roo.bootstrap.Modal} this
4050 * @param {Roo.EventObject} value
4052 "titlechanged" : true
4055 this.buttons = this.buttons || [];
4058 this.tmpl = Roo.factory(this.tmpl);
4063 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4065 title : 'test dialog',
4075 specificTitle: false,
4077 buttonPosition: 'right',
4099 editableTitle : false,
4101 onRender : function(ct, position)
4103 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4106 var cfg = Roo.apply({}, this.getAutoCreate());
4109 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4111 //if (!cfg.name.length) {
4115 cfg.cls += ' ' + this.cls;
4118 cfg.style = this.style;
4120 this.el = Roo.get(document.body).createChild(cfg, position);
4122 //var type = this.el.dom.type;
4125 if(this.tabIndex !== undefined){
4126 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4129 this.dialogEl = this.el.select('.modal-dialog',true).first();
4130 this.bodyEl = this.el.select('.modal-body',true).first();
4131 this.closeEl = this.el.select('.modal-header .close', true).first();
4132 this.headerEl = this.el.select('.modal-header',true).first();
4133 this.titleEl = this.el.select('.modal-title',true).first();
4134 this.footerEl = this.el.select('.modal-footer',true).first();
4136 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4138 //this.el.addClass("x-dlg-modal");
4140 if (this.buttons.length) {
4141 Roo.each(this.buttons, function(bb) {
4142 var b = Roo.apply({}, bb);
4143 b.xns = b.xns || Roo.bootstrap;
4144 b.xtype = b.xtype || 'Button';
4145 if (typeof(b.listeners) == 'undefined') {
4146 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4149 var btn = Roo.factory(b);
4151 btn.render(this.getButtonContainer());
4155 // render the children.
4158 if(typeof(this.items) != 'undefined'){
4159 var items = this.items;
4162 for(var i =0;i < items.length;i++) {
4163 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4167 this.items = nitems;
4169 // where are these used - they used to be body/close/footer
4173 //this.el.addClass([this.fieldClass, this.cls]);
4177 getAutoCreate : function()
4179 // we will default to modal-body-overflow - might need to remove or make optional later.
4181 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4182 html : this.html || ''
4187 cls : 'modal-title',
4191 if(this.specificTitle){ // WTF is this?
4196 if (this.allow_close && Roo.bootstrap.version == 3) {
4206 if (this.editableTitle) {
4208 cls: 'form-control roo-editable-title d-none',
4214 if (this.allow_close && Roo.bootstrap.version == 4) {
4224 if(this.size.length){
4225 size = 'modal-' + this.size;
4228 var footer = Roo.bootstrap.version == 3 ?
4230 cls : 'modal-footer',
4234 cls: 'btn-' + this.buttonPosition
4239 { // BS4 uses mr-auto on left buttons....
4240 cls : 'modal-footer'
4251 cls: "modal-dialog " + size,
4254 cls : "modal-content",
4257 cls : 'modal-header',
4272 modal.cls += ' fade';
4278 getChildContainer : function() {
4283 getButtonContainer : function() {
4285 return Roo.bootstrap.version == 4 ?
4286 this.el.select('.modal-footer',true).first()
4287 : this.el.select('.modal-footer div',true).first();
4290 initEvents : function()
4292 if (this.allow_close) {
4293 this.closeEl.on('click', this.hide, this);
4295 Roo.EventManager.onWindowResize(this.resize, this, true);
4296 if (this.editableTitle) {
4297 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4298 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4299 this.headerEditEl.on('keyup', function(e) {
4300 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4301 this.toggleHeaderInput(false)
4304 this.headerEditEl.on('blur', function(e) {
4305 this.toggleHeaderInput(false)
4314 this.maskEl.setSize(
4315 Roo.lib.Dom.getViewWidth(true),
4316 Roo.lib.Dom.getViewHeight(true)
4319 if (this.fitwindow) {
4321 this.dialogEl.setStyle( { 'max-width' : '100%' });
4323 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4324 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4329 if(this.max_width !== 0) {
4331 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4334 this.setSize(w, this.height);
4338 if(this.max_height) {
4339 this.setSize(w,Math.min(
4341 Roo.lib.Dom.getViewportHeight(true) - 60
4347 if(!this.fit_content) {
4348 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4352 this.setSize(w, Math.min(
4354 this.headerEl.getHeight() +
4355 this.footerEl.getHeight() +
4356 this.getChildHeight(this.bodyEl.dom.childNodes),
4357 Roo.lib.Dom.getViewportHeight(true) - 60)
4363 setSize : function(w,h)
4374 if (!this.rendered) {
4377 this.toggleHeaderInput(false);
4378 //this.el.setStyle('display', 'block');
4379 this.el.removeClass('hideing');
4380 this.el.dom.style.display='block';
4382 Roo.get(document.body).addClass('modal-open');
4384 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4387 this.el.addClass('show');
4388 this.el.addClass('in');
4391 this.el.addClass('show');
4392 this.el.addClass('in');
4395 // not sure how we can show data in here..
4397 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4400 Roo.get(document.body).addClass("x-body-masked");
4402 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4403 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4404 this.maskEl.dom.style.display = 'block';
4405 this.maskEl.addClass('show');
4410 this.fireEvent('show', this);
4412 // set zindex here - otherwise it appears to be ignored...
4413 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4416 this.items.forEach( function(e) {
4417 e.layout ? e.layout() : false;
4425 if(this.fireEvent("beforehide", this) !== false){
4427 this.maskEl.removeClass('show');
4429 this.maskEl.dom.style.display = '';
4430 Roo.get(document.body).removeClass("x-body-masked");
4431 this.el.removeClass('in');
4432 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4434 if(this.animate){ // why
4435 this.el.addClass('hideing');
4436 this.el.removeClass('show');
4438 if (!this.el.hasClass('hideing')) {
4439 return; // it's been shown again...
4442 this.el.dom.style.display='';
4444 Roo.get(document.body).removeClass('modal-open');
4445 this.el.removeClass('hideing');
4449 this.el.removeClass('show');
4450 this.el.dom.style.display='';
4451 Roo.get(document.body).removeClass('modal-open');
4454 this.fireEvent('hide', this);
4457 isVisible : function()
4460 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4464 addButton : function(str, cb)
4468 var b = Roo.apply({}, { html : str } );
4469 b.xns = b.xns || Roo.bootstrap;
4470 b.xtype = b.xtype || 'Button';
4471 if (typeof(b.listeners) == 'undefined') {
4472 b.listeners = { click : cb.createDelegate(this) };
4475 var btn = Roo.factory(b);
4477 btn.render(this.getButtonContainer());
4483 setDefaultButton : function(btn)
4485 //this.el.select('.modal-footer').()
4488 resizeTo: function(w,h)
4490 this.dialogEl.setWidth(w);
4492 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4494 this.bodyEl.setHeight(h - diff);
4496 this.fireEvent('resize', this);
4499 setContentSize : function(w, h)
4503 onButtonClick: function(btn,e)
4506 this.fireEvent('btnclick', btn.name, e);
4509 * Set the title of the Dialog
4510 * @param {String} str new Title
4512 setTitle: function(str) {
4513 this.titleEl.dom.innerHTML = str;
4517 * Set the body of the Dialog
4518 * @param {String} str new Title
4520 setBody: function(str) {
4521 this.bodyEl.dom.innerHTML = str;
4524 * Set the body of the Dialog using the template
4525 * @param {Obj} data - apply this data to the template and replace the body contents.
4527 applyBody: function(obj)
4530 Roo.log("Error - using apply Body without a template");
4533 this.tmpl.overwrite(this.bodyEl, obj);
4536 getChildHeight : function(child_nodes)
4540 child_nodes.length == 0
4545 var child_height = 0;
4547 for(var i = 0; i < child_nodes.length; i++) {
4550 * for modal with tabs...
4551 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4553 var layout_childs = child_nodes[i].childNodes;
4555 for(var j = 0; j < layout_childs.length; j++) {
4557 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4559 var layout_body_childs = layout_childs[j].childNodes;
4561 for(var k = 0; k < layout_body_childs.length; k++) {
4563 if(layout_body_childs[k].classList.contains('navbar')) {
4564 child_height += layout_body_childs[k].offsetHeight;
4568 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4570 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4572 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4574 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4575 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4590 child_height += child_nodes[i].offsetHeight;
4591 // Roo.log(child_nodes[i].offsetHeight);
4594 return child_height;
4596 toggleHeaderInput : function(is_edit)
4598 if (!this.editableTitle) {
4599 return; // not editable.
4601 if (is_edit && this.is_header_editing) {
4602 return; // already editing..
4606 this.headerEditEl.dom.value = this.title;
4607 this.headerEditEl.removeClass('d-none');
4608 this.headerEditEl.dom.focus();
4609 this.titleEl.addClass('d-none');
4611 this.is_header_editing = true;
4614 // flip back to not editing.
4615 this.title = this.headerEditEl.dom.value;
4616 this.headerEditEl.addClass('d-none');
4617 this.titleEl.removeClass('d-none');
4618 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4619 this.is_header_editing = false;
4620 this.fireEvent('titlechanged', this, this.title);
4629 Roo.apply(Roo.bootstrap.Modal, {
4631 * Button config that displays a single OK button
4640 * Button config that displays Yes and No buttons
4656 * Button config that displays OK and Cancel buttons
4671 * Button config that displays Yes, No and Cancel buttons
4696 * messagebox - can be used as a replace
4700 * @class Roo.MessageBox
4701 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4705 Roo.Msg.alert('Status', 'Changes saved successfully.');
4707 // Prompt for user data:
4708 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4710 // process text value...
4714 // Show a dialog using config options:
4716 title:'Save Changes?',
4717 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4718 buttons: Roo.Msg.YESNOCANCEL,
4725 Roo.bootstrap.MessageBox = function(){
4726 var dlg, opt, mask, waitTimer;
4727 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4728 var buttons, activeTextEl, bwidth;
4732 var handleButton = function(button){
4734 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4738 var handleHide = function(){
4740 dlg.el.removeClass(opt.cls);
4743 // Roo.TaskMgr.stop(waitTimer);
4744 // waitTimer = null;
4749 var updateButtons = function(b){
4752 buttons["ok"].hide();
4753 buttons["cancel"].hide();
4754 buttons["yes"].hide();
4755 buttons["no"].hide();
4756 dlg.footerEl.hide();
4760 dlg.footerEl.show();
4761 for(var k in buttons){
4762 if(typeof buttons[k] != "function"){
4765 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4766 width += buttons[k].el.getWidth()+15;
4776 var handleEsc = function(d, k, e){
4777 if(opt && opt.closable !== false){
4787 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4788 * @return {Roo.BasicDialog} The BasicDialog element
4790 getDialog : function(){
4792 dlg = new Roo.bootstrap.Modal( {
4795 //constraintoviewport:false,
4797 //collapsible : false,
4802 //buttonAlign:"center",
4803 closeClick : function(){
4804 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4807 handleButton("cancel");
4812 dlg.on("hide", handleHide);
4814 //dlg.addKeyListener(27, handleEsc);
4816 this.buttons = buttons;
4817 var bt = this.buttonText;
4818 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4819 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4820 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4821 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4823 bodyEl = dlg.bodyEl.createChild({
4825 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4826 '<textarea class="roo-mb-textarea"></textarea>' +
4827 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4829 msgEl = bodyEl.dom.firstChild;
4830 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4831 textboxEl.enableDisplayMode();
4832 textboxEl.addKeyListener([10,13], function(){
4833 if(dlg.isVisible() && opt && opt.buttons){
4836 }else if(opt.buttons.yes){
4837 handleButton("yes");
4841 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4842 textareaEl.enableDisplayMode();
4843 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4844 progressEl.enableDisplayMode();
4846 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4847 var pf = progressEl.dom.firstChild;
4849 pp = Roo.get(pf.firstChild);
4850 pp.setHeight(pf.offsetHeight);
4858 * Updates the message box body text
4859 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4860 * the XHTML-compliant non-breaking space character '&#160;')
4861 * @return {Roo.MessageBox} This message box
4863 updateText : function(text)
4865 if(!dlg.isVisible() && !opt.width){
4866 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4867 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4869 msgEl.innerHTML = text || ' ';
4871 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4872 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4874 Math.min(opt.width || cw , this.maxWidth),
4875 Math.max(opt.minWidth || this.minWidth, bwidth)
4878 activeTextEl.setWidth(w);
4880 if(dlg.isVisible()){
4881 dlg.fixedcenter = false;
4883 // to big, make it scroll. = But as usual stupid IE does not support
4886 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4887 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4888 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4890 bodyEl.dom.style.height = '';
4891 bodyEl.dom.style.overflowY = '';
4894 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4896 bodyEl.dom.style.overflowX = '';
4899 dlg.setContentSize(w, bodyEl.getHeight());
4900 if(dlg.isVisible()){
4901 dlg.fixedcenter = true;
4907 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4908 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4909 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4910 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4911 * @return {Roo.MessageBox} This message box
4913 updateProgress : function(value, text){
4915 this.updateText(text);
4918 if (pp) { // weird bug on my firefox - for some reason this is not defined
4919 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4920 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4926 * Returns true if the message box is currently displayed
4927 * @return {Boolean} True if the message box is visible, else false
4929 isVisible : function(){
4930 return dlg && dlg.isVisible();
4934 * Hides the message box if it is displayed
4937 if(this.isVisible()){
4943 * Displays a new message box, or reinitializes an existing message box, based on the config options
4944 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4945 * The following config object properties are supported:
4947 Property Type Description
4948 ---------- --------------- ------------------------------------------------------------------------------------
4949 animEl String/Element An id or Element from which the message box should animate as it opens and
4950 closes (defaults to undefined)
4951 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4952 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4953 closable Boolean False to hide the top-right close button (defaults to true). Note that
4954 progress and wait dialogs will ignore this property and always hide the
4955 close button as they can only be closed programmatically.
4956 cls String A custom CSS class to apply to the message box element
4957 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4958 displayed (defaults to 75)
4959 fn Function A callback function to execute after closing the dialog. The arguments to the
4960 function will be btn (the name of the button that was clicked, if applicable,
4961 e.g. "ok"), and text (the value of the active text field, if applicable).
4962 Progress and wait dialogs will ignore this option since they do not respond to
4963 user actions and can only be closed programmatically, so any required function
4964 should be called by the same code after it closes the dialog.
4965 icon String A CSS class that provides a background image to be used as an icon for
4966 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4967 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4968 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4969 modal Boolean False to allow user interaction with the page while the message box is
4970 displayed (defaults to true)
4971 msg String A string that will replace the existing message box body text (defaults
4972 to the XHTML-compliant non-breaking space character ' ')
4973 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4974 progress Boolean True to display a progress bar (defaults to false)
4975 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4976 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4977 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4978 title String The title text
4979 value String The string value to set into the active textbox element if displayed
4980 wait Boolean True to display a progress bar (defaults to false)
4981 width Number The width of the dialog in pixels
4988 msg: 'Please enter your address:',
4990 buttons: Roo.MessageBox.OKCANCEL,
4993 animEl: 'addAddressBtn'
4996 * @param {Object} config Configuration options
4997 * @return {Roo.MessageBox} This message box
4999 show : function(options)
5002 // this causes nightmares if you show one dialog after another
5003 // especially on callbacks..
5005 if(this.isVisible()){
5008 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5009 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5010 Roo.log("New Dialog Message:" + options.msg )
5011 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5012 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5015 var d = this.getDialog();
5017 d.setTitle(opt.title || " ");
5018 d.closeEl.setDisplayed(opt.closable !== false);
5019 activeTextEl = textboxEl;
5020 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5025 textareaEl.setHeight(typeof opt.multiline == "number" ?
5026 opt.multiline : this.defaultTextHeight);
5027 activeTextEl = textareaEl;
5036 progressEl.setDisplayed(opt.progress === true);
5038 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5040 this.updateProgress(0);
5041 activeTextEl.dom.value = opt.value || "";
5043 dlg.setDefaultButton(activeTextEl);
5045 var bs = opt.buttons;
5049 }else if(bs && bs.yes){
5050 db = buttons["yes"];
5052 dlg.setDefaultButton(db);
5054 bwidth = updateButtons(opt.buttons);
5055 this.updateText(opt.msg);
5057 d.el.addClass(opt.cls);
5059 d.proxyDrag = opt.proxyDrag === true;
5060 d.modal = opt.modal !== false;
5061 d.mask = opt.modal !== false ? mask : false;
5063 // force it to the end of the z-index stack so it gets a cursor in FF
5064 document.body.appendChild(dlg.el.dom);
5065 d.animateTarget = null;
5066 d.show(options.animEl);
5072 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5073 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5074 * and closing the message box when the process is complete.
5075 * @param {String} title The title bar text
5076 * @param {String} msg The message box body text
5077 * @return {Roo.MessageBox} This message box
5079 progress : function(title, msg){
5086 minWidth: this.minProgressWidth,
5093 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5094 * If a callback function is passed it will be called after the user clicks the button, and the
5095 * id of the button that was clicked will be passed as the only parameter to the callback
5096 * (could also be the top-right close button).
5097 * @param {String} title The title bar text
5098 * @param {String} msg The message box body text
5099 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5100 * @param {Object} scope (optional) The scope of the callback function
5101 * @return {Roo.MessageBox} This message box
5103 alert : function(title, msg, fn, scope)
5118 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5119 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5120 * You are responsible for closing the message box when the process is complete.
5121 * @param {String} msg The message box body text
5122 * @param {String} title (optional) The title bar text
5123 * @return {Roo.MessageBox} This message box
5125 wait : function(msg, title){
5136 waitTimer = Roo.TaskMgr.start({
5138 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5146 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5147 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5148 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5149 * @param {String} title The title bar text
5150 * @param {String} msg The message box body text
5151 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5152 * @param {Object} scope (optional) The scope of the callback function
5153 * @return {Roo.MessageBox} This message box
5155 confirm : function(title, msg, fn, scope){
5159 buttons: this.YESNO,
5168 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5169 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5170 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5171 * (could also be the top-right close button) and the text that was entered will be passed as the two
5172 * parameters to the callback.
5173 * @param {String} title The title bar text
5174 * @param {String} msg The message box body text
5175 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5176 * @param {Object} scope (optional) The scope of the callback function
5177 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5178 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5179 * @return {Roo.MessageBox} This message box
5181 prompt : function(title, msg, fn, scope, multiline){
5185 buttons: this.OKCANCEL,
5190 multiline: multiline,
5197 * Button config that displays a single OK button
5202 * Button config that displays Yes and No buttons
5205 YESNO : {yes:true, no:true},
5207 * Button config that displays OK and Cancel buttons
5210 OKCANCEL : {ok:true, cancel:true},
5212 * Button config that displays Yes, No and Cancel buttons
5215 YESNOCANCEL : {yes:true, no:true, cancel:true},
5218 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5221 defaultTextHeight : 75,
5223 * The maximum width in pixels of the message box (defaults to 600)
5228 * The minimum width in pixels of the message box (defaults to 100)
5233 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5234 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5237 minProgressWidth : 250,
5239 * An object containing the default button text strings that can be overriden for localized language support.
5240 * Supported properties are: ok, cancel, yes and no.
5241 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5254 * Shorthand for {@link Roo.MessageBox}
5256 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5257 Roo.Msg = Roo.Msg || Roo.MessageBox;
5266 * @class Roo.bootstrap.Navbar
5267 * @extends Roo.bootstrap.Component
5268 * Bootstrap Navbar class
5271 * Create a new Navbar
5272 * @param {Object} config The config object
5276 Roo.bootstrap.Navbar = function(config){
5277 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5281 * @event beforetoggle
5282 * Fire before toggle the menu
5283 * @param {Roo.EventObject} e
5285 "beforetoggle" : true
5289 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5298 getAutoCreate : function(){
5301 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5305 initEvents :function ()
5307 //Roo.log(this.el.select('.navbar-toggle',true));
5308 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5315 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5317 var size = this.el.getSize();
5318 this.maskEl.setSize(size.width, size.height);
5319 this.maskEl.enableDisplayMode("block");
5328 getChildContainer : function()
5330 if (this.el && this.el.select('.collapse').getCount()) {
5331 return this.el.select('.collapse',true).first();
5346 onToggle : function()
5349 if(this.fireEvent('beforetoggle', this) === false){
5352 var ce = this.el.select('.navbar-collapse',true).first();
5354 if (!ce.hasClass('show')) {
5364 * Expand the navbar pulldown
5366 expand : function ()
5369 var ce = this.el.select('.navbar-collapse',true).first();
5370 if (ce.hasClass('collapsing')) {
5373 ce.dom.style.height = '';
5375 ce.addClass('in'); // old...
5376 ce.removeClass('collapse');
5377 ce.addClass('show');
5378 var h = ce.getHeight();
5380 ce.removeClass('show');
5381 // at this point we should be able to see it..
5382 ce.addClass('collapsing');
5384 ce.setHeight(0); // resize it ...
5385 ce.on('transitionend', function() {
5386 //Roo.log('done transition');
5387 ce.removeClass('collapsing');
5388 ce.addClass('show');
5389 ce.removeClass('collapse');
5391 ce.dom.style.height = '';
5392 }, this, { single: true} );
5394 ce.dom.scrollTop = 0;
5397 * Collapse the navbar pulldown
5399 collapse : function()
5401 var ce = this.el.select('.navbar-collapse',true).first();
5403 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5404 // it's collapsed or collapsing..
5407 ce.removeClass('in'); // old...
5408 ce.setHeight(ce.getHeight());
5409 ce.removeClass('show');
5410 ce.addClass('collapsing');
5412 ce.on('transitionend', function() {
5413 ce.dom.style.height = '';
5414 ce.removeClass('collapsing');
5415 ce.addClass('collapse');
5416 }, this, { single: true} );
5436 * @class Roo.bootstrap.NavSimplebar
5437 * @extends Roo.bootstrap.Navbar
5438 * Bootstrap Sidebar class
5440 * @cfg {Boolean} inverse is inverted color
5442 * @cfg {String} type (nav | pills | tabs)
5443 * @cfg {Boolean} arrangement stacked | justified
5444 * @cfg {String} align (left | right) alignment
5446 * @cfg {Boolean} main (true|false) main nav bar? default false
5447 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5449 * @cfg {String} tag (header|footer|nav|div) default is nav
5451 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5455 * Create a new Sidebar
5456 * @param {Object} config The config object
5460 Roo.bootstrap.NavSimplebar = function(config){
5461 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5464 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5480 getAutoCreate : function(){
5484 tag : this.tag || 'div',
5485 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5487 if (['light','white'].indexOf(this.weight) > -1) {
5488 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5490 cfg.cls += ' bg-' + this.weight;
5493 cfg.cls += ' navbar-inverse';
5497 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5499 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5508 cls: 'nav nav-' + this.xtype,
5514 this.type = this.type || 'nav';
5515 if (['tabs','pills'].indexOf(this.type) != -1) {
5516 cfg.cn[0].cls += ' nav-' + this.type
5520 if (this.type!=='nav') {
5521 Roo.log('nav type must be nav/tabs/pills')
5523 cfg.cn[0].cls += ' navbar-nav'
5529 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5530 cfg.cn[0].cls += ' nav-' + this.arrangement;
5534 if (this.align === 'right') {
5535 cfg.cn[0].cls += ' navbar-right';
5560 * navbar-expand-md fixed-top
5564 * @class Roo.bootstrap.NavHeaderbar
5565 * @extends Roo.bootstrap.NavSimplebar
5566 * Bootstrap Sidebar class
5568 * @cfg {String} brand what is brand
5569 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5570 * @cfg {String} brand_href href of the brand
5571 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5572 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5573 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5574 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5577 * Create a new Sidebar
5578 * @param {Object} config The config object
5582 Roo.bootstrap.NavHeaderbar = function(config){
5583 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5587 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5594 desktopCenter : false,
5597 getAutoCreate : function(){
5600 tag: this.nav || 'nav',
5601 cls: 'navbar navbar-expand-md',
5607 if (this.desktopCenter) {
5608 cn.push({cls : 'container', cn : []});
5616 cls: 'navbar-toggle navbar-toggler',
5617 'data-toggle': 'collapse',
5622 html: 'Toggle navigation'
5626 cls: 'icon-bar navbar-toggler-icon'
5639 cn.push( Roo.bootstrap.version == 4 ? btn : {
5641 cls: 'navbar-header',
5650 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5654 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5656 if (['light','white'].indexOf(this.weight) > -1) {
5657 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5659 cfg.cls += ' bg-' + this.weight;
5662 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5663 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5665 // tag can override this..
5667 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5670 if (this.brand !== '') {
5671 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5672 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5674 href: this.brand_href ? this.brand_href : '#',
5675 cls: 'navbar-brand',
5683 cfg.cls += ' main-nav';
5691 getHeaderChildContainer : function()
5693 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5694 return this.el.select('.navbar-header',true).first();
5697 return this.getChildContainer();
5700 getChildContainer : function()
5703 return this.el.select('.roo-navbar-collapse',true).first();
5708 initEvents : function()
5710 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5712 if (this.autohide) {
5717 Roo.get(document).on('scroll',function(e) {
5718 var ns = Roo.get(document).getScroll().top;
5719 var os = prevScroll;
5723 ft.removeClass('slideDown');
5724 ft.addClass('slideUp');
5727 ft.removeClass('slideUp');
5728 ft.addClass('slideDown');
5749 * @class Roo.bootstrap.NavSidebar
5750 * @extends Roo.bootstrap.Navbar
5751 * Bootstrap Sidebar class
5754 * Create a new Sidebar
5755 * @param {Object} config The config object
5759 Roo.bootstrap.NavSidebar = function(config){
5760 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5763 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5765 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5767 getAutoCreate : function(){
5772 cls: 'sidebar sidebar-nav'
5794 * @class Roo.bootstrap.NavGroup
5795 * @extends Roo.bootstrap.Component
5796 * Bootstrap NavGroup class
5797 * @cfg {String} align (left|right)
5798 * @cfg {Boolean} inverse
5799 * @cfg {String} type (nav|pills|tab) default nav
5800 * @cfg {String} navId - reference Id for navbar.
5801 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5804 * Create a new nav group
5805 * @param {Object} config The config object
5808 Roo.bootstrap.NavGroup = function(config){
5809 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5812 Roo.bootstrap.NavGroup.register(this);
5816 * Fires when the active item changes
5817 * @param {Roo.bootstrap.NavGroup} this
5818 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5819 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5826 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5838 getAutoCreate : function()
5840 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5846 if (Roo.bootstrap.version == 4) {
5847 if (['tabs','pills'].indexOf(this.type) != -1) {
5848 cfg.cls += ' nav-' + this.type;
5850 // trying to remove so header bar can right align top?
5851 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5852 // do not use on header bar...
5853 cfg.cls += ' navbar-nav';
5858 if (['tabs','pills'].indexOf(this.type) != -1) {
5859 cfg.cls += ' nav-' + this.type
5861 if (this.type !== 'nav') {
5862 Roo.log('nav type must be nav/tabs/pills')
5864 cfg.cls += ' navbar-nav'
5868 if (this.parent() && this.parent().sidebar) {
5871 cls: 'dashboard-menu sidebar-menu'
5877 if (this.form === true) {
5880 cls: 'navbar-form form-inline'
5882 //nav navbar-right ml-md-auto
5883 if (this.align === 'right') {
5884 cfg.cls += ' navbar-right ml-md-auto';
5886 cfg.cls += ' navbar-left';
5890 if (this.align === 'right') {
5891 cfg.cls += ' navbar-right ml-md-auto';
5893 cfg.cls += ' mr-auto';
5897 cfg.cls += ' navbar-inverse';
5905 * sets the active Navigation item
5906 * @param {Roo.bootstrap.NavItem} the new current navitem
5908 setActiveItem : function(item)
5911 Roo.each(this.navItems, function(v){
5916 v.setActive(false, true);
5923 item.setActive(true, true);
5924 this.fireEvent('changed', this, item, prev);
5929 * gets the active Navigation item
5930 * @return {Roo.bootstrap.NavItem} the current navitem
5932 getActive : function()
5936 Roo.each(this.navItems, function(v){
5947 indexOfNav : function()
5951 Roo.each(this.navItems, function(v,i){
5962 * adds a Navigation item
5963 * @param {Roo.bootstrap.NavItem} the navitem to add
5965 addItem : function(cfg)
5967 if (this.form && Roo.bootstrap.version == 4) {
5970 var cn = new Roo.bootstrap.NavItem(cfg);
5972 cn.parentId = this.id;
5973 cn.onRender(this.el, null);
5977 * register a Navigation item
5978 * @param {Roo.bootstrap.NavItem} the navitem to add
5980 register : function(item)
5982 this.navItems.push( item);
5983 item.navId = this.navId;
5988 * clear all the Navigation item
5991 clearAll : function()
5994 this.el.dom.innerHTML = '';
5997 getNavItem: function(tabId)
6000 Roo.each(this.navItems, function(e) {
6001 if (e.tabId == tabId) {
6011 setActiveNext : function()
6013 var i = this.indexOfNav(this.getActive());
6014 if (i > this.navItems.length) {
6017 this.setActiveItem(this.navItems[i+1]);
6019 setActivePrev : function()
6021 var i = this.indexOfNav(this.getActive());
6025 this.setActiveItem(this.navItems[i-1]);
6027 clearWasActive : function(except) {
6028 Roo.each(this.navItems, function(e) {
6029 if (e.tabId != except.tabId && e.was_active) {
6030 e.was_active = false;
6037 getWasActive : function ()
6040 Roo.each(this.navItems, function(e) {
6055 Roo.apply(Roo.bootstrap.NavGroup, {
6059 * register a Navigation Group
6060 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6062 register : function(navgrp)
6064 this.groups[navgrp.navId] = navgrp;
6068 * fetch a Navigation Group based on the navigation ID
6069 * @param {string} the navgroup to add
6070 * @returns {Roo.bootstrap.NavGroup} the navgroup
6072 get: function(navId) {
6073 if (typeof(this.groups[navId]) == 'undefined') {
6075 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6077 return this.groups[navId] ;
6092 * @class Roo.bootstrap.NavItem
6093 * @extends Roo.bootstrap.Component
6094 * Bootstrap Navbar.NavItem class
6095 * @cfg {String} href link to
6096 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6097 * @cfg {Boolean} button_outline show and outlined button
6098 * @cfg {String} html content of button
6099 * @cfg {String} badge text inside badge
6100 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6101 * @cfg {String} glyphicon DEPRICATED - use fa
6102 * @cfg {String} icon DEPRICATED - use fa
6103 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6104 * @cfg {Boolean} active Is item active
6105 * @cfg {Boolean} disabled Is item disabled
6106 * @cfg {String} linkcls Link Class
6107 * @cfg {Boolean} preventDefault (true | false) default false
6108 * @cfg {String} tabId the tab that this item activates.
6109 * @cfg {String} tagtype (a|span) render as a href or span?
6110 * @cfg {Boolean} animateRef (true|false) link to element default false
6113 * Create a new Navbar Item
6114 * @param {Object} config The config object
6116 Roo.bootstrap.NavItem = function(config){
6117 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6122 * The raw click event for the entire grid.
6123 * @param {Roo.EventObject} e
6128 * Fires when the active item active state changes
6129 * @param {Roo.bootstrap.NavItem} this
6130 * @param {boolean} state the new state
6136 * Fires when scroll to element
6137 * @param {Roo.bootstrap.NavItem} this
6138 * @param {Object} options
6139 * @param {Roo.EventObject} e
6147 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6156 preventDefault : false,
6164 button_outline : false,
6168 getAutoCreate : function(){
6175 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6178 cfg.cls += ' active' ;
6180 if (this.disabled) {
6181 cfg.cls += ' disabled';
6185 if (this.button_weight.length) {
6186 cfg.tag = this.href ? 'a' : 'button';
6187 cfg.html = this.html || '';
6188 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6190 cfg.href = this.href;
6193 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6195 cfg.cls += " nav-html";
6198 // menu .. should add dropdown-menu class - so no need for carat..
6200 if (this.badge !== '') {
6202 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6207 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6211 href : this.href || "#",
6212 html: this.html || '',
6216 if (this.tagtype == 'a') {
6217 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6221 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6222 } else if (this.fa) {
6223 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6224 } else if(this.glyphicon) {
6225 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6227 cfg.cn[0].cls += " nav-html";
6231 cfg.cn[0].html += " <span class='caret'></span>";
6235 if (this.badge !== '') {
6236 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6244 onRender : function(ct, position)
6246 // Roo.log("Call onRender: " + this.xtype);
6247 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6251 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6252 this.navLink = this.el.select('.nav-link',true).first();
6253 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6258 initEvents: function()
6260 if (typeof (this.menu) != 'undefined') {
6261 this.menu.parentType = this.xtype;
6262 this.menu.triggerEl = this.el;
6263 this.menu = this.addxtype(Roo.apply({}, this.menu));
6266 this.el.on('click', this.onClick, this);
6268 //if(this.tagtype == 'span'){
6269 // this.el.select('span',true).on('click', this.onClick, this);
6272 // at this point parent should be available..
6273 this.parent().register(this);
6276 onClick : function(e)
6278 if (e.getTarget('.dropdown-menu-item')) {
6279 // did you click on a menu itemm.... - then don't trigger onclick..
6284 this.preventDefault ||
6287 Roo.log("NavItem - prevent Default?");
6291 if (this.disabled) {
6295 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6296 if (tg && tg.transition) {
6297 Roo.log("waiting for the transitionend");
6303 //Roo.log("fire event clicked");
6304 if(this.fireEvent('click', this, e) === false){
6308 if(this.tagtype == 'span'){
6312 //Roo.log(this.href);
6313 var ael = this.el.select('a',true).first();
6316 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6317 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6318 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6319 return; // ignore... - it's a 'hash' to another page.
6321 Roo.log("NavItem - prevent Default?");
6323 this.scrollToElement(e);
6327 var p = this.parent();
6329 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6330 if (typeof(p.setActiveItem) !== 'undefined') {
6331 p.setActiveItem(this);
6335 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6336 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6337 // remove the collapsed menu expand...
6338 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6342 isActive: function () {
6345 setActive : function(state, fire, is_was_active)
6347 if (this.active && !state && this.navId) {
6348 this.was_active = true;
6349 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6351 nv.clearWasActive(this);
6355 this.active = state;
6358 this.el.removeClass('active');
6359 this.navLink ? this.navLink.removeClass('active') : false;
6360 } else if (!this.el.hasClass('active')) {
6362 this.el.addClass('active');
6363 if (Roo.bootstrap.version == 4 && this.navLink ) {
6364 this.navLink.addClass('active');
6369 this.fireEvent('changed', this, state);
6372 // show a panel if it's registered and related..
6374 if (!this.navId || !this.tabId || !state || is_was_active) {
6378 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6382 var pan = tg.getPanelByName(this.tabId);
6386 // if we can not flip to new panel - go back to old nav highlight..
6387 if (false == tg.showPanel(pan)) {
6388 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6390 var onav = nv.getWasActive();
6392 onav.setActive(true, false, true);
6401 // this should not be here...
6402 setDisabled : function(state)
6404 this.disabled = state;
6406 this.el.removeClass('disabled');
6407 } else if (!this.el.hasClass('disabled')) {
6408 this.el.addClass('disabled');
6414 * Fetch the element to display the tooltip on.
6415 * @return {Roo.Element} defaults to this.el
6417 tooltipEl : function()
6419 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6422 scrollToElement : function(e)
6424 var c = document.body;
6427 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6429 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6430 c = document.documentElement;
6433 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6439 var o = target.calcOffsetsTo(c);
6446 this.fireEvent('scrollto', this, options, e);
6448 Roo.get(c).scrollTo('top', options.value, true);
6453 * Set the HTML (text content) of the item
6454 * @param {string} html content for the nav item
6456 setHtml : function(html)
6459 this.htmlEl.dom.innerHTML = html;
6471 * <span> icon </span>
6472 * <span> text </span>
6473 * <span>badge </span>
6477 * @class Roo.bootstrap.NavSidebarItem
6478 * @extends Roo.bootstrap.NavItem
6479 * Bootstrap Navbar.NavSidebarItem class
6480 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6481 * {Boolean} open is the menu open
6482 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6483 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6484 * {String} buttonSize (sm|md|lg)the extra classes for the button
6485 * {Boolean} showArrow show arrow next to the text (default true)
6487 * Create a new Navbar Button
6488 * @param {Object} config The config object
6490 Roo.bootstrap.NavSidebarItem = function(config){
6491 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6496 * The raw click event for the entire grid.
6497 * @param {Roo.EventObject} e
6502 * Fires when the active item active state changes
6503 * @param {Roo.bootstrap.NavSidebarItem} this
6504 * @param {boolean} state the new state
6512 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6514 badgeWeight : 'default',
6520 buttonWeight : 'default',
6526 getAutoCreate : function(){
6531 href : this.href || '#',
6537 if(this.buttonView){
6540 href : this.href || '#',
6541 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6554 cfg.cls += ' active';
6557 if (this.disabled) {
6558 cfg.cls += ' disabled';
6561 cfg.cls += ' open x-open';
6564 if (this.glyphicon || this.icon) {
6565 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6566 a.cn.push({ tag : 'i', cls : c }) ;
6569 if(!this.buttonView){
6572 html : this.html || ''
6579 if (this.badge !== '') {
6580 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6586 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6589 a.cls += ' dropdown-toggle treeview' ;
6595 initEvents : function()
6597 if (typeof (this.menu) != 'undefined') {
6598 this.menu.parentType = this.xtype;
6599 this.menu.triggerEl = this.el;
6600 this.menu = this.addxtype(Roo.apply({}, this.menu));
6603 this.el.on('click', this.onClick, this);
6605 if(this.badge !== ''){
6606 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6611 onClick : function(e)
6618 if(this.preventDefault){
6622 this.fireEvent('click', this, e);
6625 disable : function()
6627 this.setDisabled(true);
6632 this.setDisabled(false);
6635 setDisabled : function(state)
6637 if(this.disabled == state){
6641 this.disabled = state;
6644 this.el.addClass('disabled');
6648 this.el.removeClass('disabled');
6653 setActive : function(state)
6655 if(this.active == state){
6659 this.active = state;
6662 this.el.addClass('active');
6666 this.el.removeClass('active');
6671 isActive: function ()
6676 setBadge : function(str)
6682 this.badgeEl.dom.innerHTML = str;
6697 Roo.namespace('Roo.bootstrap.breadcrumb');
6701 * @class Roo.bootstrap.breadcrumb.Nav
6702 * @extends Roo.bootstrap.Component
6703 * Bootstrap Breadcrumb Nav Class
6705 * @children Roo.bootstrap.breadcrumb.Item
6708 * Create a new breadcrumb.Nav
6709 * @param {Object} config The config object
6713 Roo.bootstrap.breadcrumb.Nav = function(config){
6714 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6719 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6721 getAutoCreate : function()
6738 initEvents: function()
6740 this.olEl = this.el.select('ol',true).first();
6742 getChildContainer : function()
6758 * @class Roo.bootstrap.breadcrumb.Nav
6759 * @extends Roo.bootstrap.Component
6760 * Bootstrap Breadcrumb Nav Class
6762 * @children Roo.bootstrap.breadcrumb.Component
6763 * @cfg {String} html the content of the link.
6764 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6765 * @cfg {Boolean} active is it active
6769 * Create a new breadcrumb.Nav
6770 * @param {Object} config The config object
6773 Roo.bootstrap.breadcrumb.Item = function(config){
6774 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6779 * The img click event for the img.
6780 * @param {Roo.EventObject} e
6787 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6792 getAutoCreate : function()
6797 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6799 if (this.href !== false) {
6806 cfg.html = this.html;
6812 initEvents: function()
6815 this.el.select('a', true).first().on('click',this.onClick, this)
6819 onClick : function(e)
6822 this.fireEvent('click',this, e);
6835 * @class Roo.bootstrap.Row
6836 * @extends Roo.bootstrap.Component
6837 * Bootstrap Row class (contains columns...)
6841 * @param {Object} config The config object
6844 Roo.bootstrap.Row = function(config){
6845 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6848 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6850 getAutoCreate : function(){
6869 * @class Roo.bootstrap.Pagination
6870 * @extends Roo.bootstrap.Component
6871 * Bootstrap Pagination class
6872 * @cfg {String} size xs | sm | md | lg
6873 * @cfg {Boolean} inverse false | true
6876 * Create a new Pagination
6877 * @param {Object} config The config object
6880 Roo.bootstrap.Pagination = function(config){
6881 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6884 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6890 getAutoCreate : function(){
6896 cfg.cls += ' inverse';
6902 cfg.cls += " " + this.cls;
6920 * @class Roo.bootstrap.PaginationItem
6921 * @extends Roo.bootstrap.Component
6922 * Bootstrap PaginationItem class
6923 * @cfg {String} html text
6924 * @cfg {String} href the link
6925 * @cfg {Boolean} preventDefault (true | false) default true
6926 * @cfg {Boolean} active (true | false) default false
6927 * @cfg {Boolean} disabled default false
6931 * Create a new PaginationItem
6932 * @param {Object} config The config object
6936 Roo.bootstrap.PaginationItem = function(config){
6937 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6942 * The raw click event for the entire grid.
6943 * @param {Roo.EventObject} e
6949 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6953 preventDefault: true,
6958 getAutoCreate : function(){
6964 href : this.href ? this.href : '#',
6965 html : this.html ? this.html : ''
6975 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6979 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6985 initEvents: function() {
6987 this.el.on('click', this.onClick, this);
6990 onClick : function(e)
6992 Roo.log('PaginationItem on click ');
6993 if(this.preventDefault){
7001 this.fireEvent('click', this, e);
7017 * @class Roo.bootstrap.Slider
7018 * @extends Roo.bootstrap.Component
7019 * Bootstrap Slider class
7022 * Create a new Slider
7023 * @param {Object} config The config object
7026 Roo.bootstrap.Slider = function(config){
7027 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7030 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7032 getAutoCreate : function(){
7036 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7040 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7052 * Ext JS Library 1.1.1
7053 * Copyright(c) 2006-2007, Ext JS, LLC.
7055 * Originally Released Under LGPL - original licence link has changed is not relivant.
7058 * <script type="text/javascript">
7063 * @class Roo.grid.ColumnModel
7064 * @extends Roo.util.Observable
7065 * This is the default implementation of a ColumnModel used by the Grid. It defines
7066 * the columns in the grid.
7069 var colModel = new Roo.grid.ColumnModel([
7070 {header: "Ticker", width: 60, sortable: true, locked: true},
7071 {header: "Company Name", width: 150, sortable: true},
7072 {header: "Market Cap.", width: 100, sortable: true},
7073 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7074 {header: "Employees", width: 100, sortable: true, resizable: false}
7079 * The config options listed for this class are options which may appear in each
7080 * individual column definition.
7081 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7083 * @param {Object} config An Array of column config objects. See this class's
7084 * config objects for details.
7086 Roo.grid.ColumnModel = function(config){
7088 * The config passed into the constructor
7090 this.config = config;
7093 // if no id, create one
7094 // if the column does not have a dataIndex mapping,
7095 // map it to the order it is in the config
7096 for(var i = 0, len = config.length; i < len; i++){
7098 if(typeof c.dataIndex == "undefined"){
7101 if(typeof c.renderer == "string"){
7102 c.renderer = Roo.util.Format[c.renderer];
7104 if(typeof c.id == "undefined"){
7107 if(c.editor && c.editor.xtype){
7108 c.editor = Roo.factory(c.editor, Roo.grid);
7110 if(c.editor && c.editor.isFormField){
7111 c.editor = new Roo.grid.GridEditor(c.editor);
7113 this.lookup[c.id] = c;
7117 * The width of columns which have no width specified (defaults to 100)
7120 this.defaultWidth = 100;
7123 * Default sortable of columns which have no sortable specified (defaults to false)
7126 this.defaultSortable = false;
7130 * @event widthchange
7131 * Fires when the width of a column changes.
7132 * @param {ColumnModel} this
7133 * @param {Number} columnIndex The column index
7134 * @param {Number} newWidth The new width
7136 "widthchange": true,
7138 * @event headerchange
7139 * Fires when the text of a header changes.
7140 * @param {ColumnModel} this
7141 * @param {Number} columnIndex The column index
7142 * @param {Number} newText The new header text
7144 "headerchange": true,
7146 * @event hiddenchange
7147 * Fires when a column is hidden or "unhidden".
7148 * @param {ColumnModel} this
7149 * @param {Number} columnIndex The column index
7150 * @param {Boolean} hidden true if hidden, false otherwise
7152 "hiddenchange": true,
7154 * @event columnmoved
7155 * Fires when a column is moved.
7156 * @param {ColumnModel} this
7157 * @param {Number} oldIndex
7158 * @param {Number} newIndex
7160 "columnmoved" : true,
7162 * @event columlockchange
7163 * Fires when a column's locked state is changed
7164 * @param {ColumnModel} this
7165 * @param {Number} colIndex
7166 * @param {Boolean} locked true if locked
7168 "columnlockchange" : true
7170 Roo.grid.ColumnModel.superclass.constructor.call(this);
7172 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7174 * @cfg {String} header The header text to display in the Grid view.
7177 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7178 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7179 * specified, the column's index is used as an index into the Record's data Array.
7182 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7183 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7186 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7187 * Defaults to the value of the {@link #defaultSortable} property.
7188 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7191 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7194 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7197 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7200 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7203 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7204 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7205 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7206 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7209 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7212 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7215 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7218 * @cfg {String} cursor (Optional)
7221 * @cfg {String} tooltip (Optional)
7224 * @cfg {Number} xs (Optional)
7227 * @cfg {Number} sm (Optional)
7230 * @cfg {Number} md (Optional)
7233 * @cfg {Number} lg (Optional)
7236 * Returns the id of the column at the specified index.
7237 * @param {Number} index The column index
7238 * @return {String} the id
7240 getColumnId : function(index){
7241 return this.config[index].id;
7245 * Returns the column for a specified id.
7246 * @param {String} id The column id
7247 * @return {Object} the column
7249 getColumnById : function(id){
7250 return this.lookup[id];
7255 * Returns the column for a specified dataIndex.
7256 * @param {String} dataIndex The column dataIndex
7257 * @return {Object|Boolean} the column or false if not found
7259 getColumnByDataIndex: function(dataIndex){
7260 var index = this.findColumnIndex(dataIndex);
7261 return index > -1 ? this.config[index] : false;
7265 * Returns the index for a specified column id.
7266 * @param {String} id The column id
7267 * @return {Number} the index, or -1 if not found
7269 getIndexById : function(id){
7270 for(var i = 0, len = this.config.length; i < len; i++){
7271 if(this.config[i].id == id){
7279 * Returns the index for a specified column dataIndex.
7280 * @param {String} dataIndex The column dataIndex
7281 * @return {Number} the index, or -1 if not found
7284 findColumnIndex : function(dataIndex){
7285 for(var i = 0, len = this.config.length; i < len; i++){
7286 if(this.config[i].dataIndex == dataIndex){
7294 moveColumn : function(oldIndex, newIndex){
7295 var c = this.config[oldIndex];
7296 this.config.splice(oldIndex, 1);
7297 this.config.splice(newIndex, 0, c);
7298 this.dataMap = null;
7299 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7302 isLocked : function(colIndex){
7303 return this.config[colIndex].locked === true;
7306 setLocked : function(colIndex, value, suppressEvent){
7307 if(this.isLocked(colIndex) == value){
7310 this.config[colIndex].locked = value;
7312 this.fireEvent("columnlockchange", this, colIndex, value);
7316 getTotalLockedWidth : function(){
7318 for(var i = 0; i < this.config.length; i++){
7319 if(this.isLocked(i) && !this.isHidden(i)){
7320 this.totalWidth += this.getColumnWidth(i);
7326 getLockedCount : function(){
7327 for(var i = 0, len = this.config.length; i < len; i++){
7328 if(!this.isLocked(i)){
7333 return this.config.length;
7337 * Returns the number of columns.
7340 getColumnCount : function(visibleOnly){
7341 if(visibleOnly === true){
7343 for(var i = 0, len = this.config.length; i < len; i++){
7344 if(!this.isHidden(i)){
7350 return this.config.length;
7354 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7355 * @param {Function} fn
7356 * @param {Object} scope (optional)
7357 * @return {Array} result
7359 getColumnsBy : function(fn, scope){
7361 for(var i = 0, len = this.config.length; i < len; i++){
7362 var c = this.config[i];
7363 if(fn.call(scope||this, c, i) === true){
7371 * Returns true if the specified column is sortable.
7372 * @param {Number} col The column index
7375 isSortable : function(col){
7376 if(typeof this.config[col].sortable == "undefined"){
7377 return this.defaultSortable;
7379 return this.config[col].sortable;
7383 * Returns the rendering (formatting) function defined for the column.
7384 * @param {Number} col The column index.
7385 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7387 getRenderer : function(col){
7388 if(!this.config[col].renderer){
7389 return Roo.grid.ColumnModel.defaultRenderer;
7391 return this.config[col].renderer;
7395 * Sets the rendering (formatting) function for a column.
7396 * @param {Number} col The column index
7397 * @param {Function} fn The function to use to process the cell's raw data
7398 * to return HTML markup for the grid view. The render function is called with
7399 * the following parameters:<ul>
7400 * <li>Data value.</li>
7401 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7402 * <li>css A CSS style string to apply to the table cell.</li>
7403 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7404 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7405 * <li>Row index</li>
7406 * <li>Column index</li>
7407 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7409 setRenderer : function(col, fn){
7410 this.config[col].renderer = fn;
7414 * Returns the width for the specified column.
7415 * @param {Number} col The column index
7418 getColumnWidth : function(col){
7419 return this.config[col].width * 1 || this.defaultWidth;
7423 * Sets the width for a column.
7424 * @param {Number} col The column index
7425 * @param {Number} width The new width
7427 setColumnWidth : function(col, width, suppressEvent){
7428 this.config[col].width = width;
7429 this.totalWidth = null;
7431 this.fireEvent("widthchange", this, col, width);
7436 * Returns the total width of all columns.
7437 * @param {Boolean} includeHidden True to include hidden column widths
7440 getTotalWidth : function(includeHidden){
7441 if(!this.totalWidth){
7442 this.totalWidth = 0;
7443 for(var i = 0, len = this.config.length; i < len; i++){
7444 if(includeHidden || !this.isHidden(i)){
7445 this.totalWidth += this.getColumnWidth(i);
7449 return this.totalWidth;
7453 * Returns the header for the specified column.
7454 * @param {Number} col The column index
7457 getColumnHeader : function(col){
7458 return this.config[col].header;
7462 * Sets the header for a column.
7463 * @param {Number} col The column index
7464 * @param {String} header The new header
7466 setColumnHeader : function(col, header){
7467 this.config[col].header = header;
7468 this.fireEvent("headerchange", this, col, header);
7472 * Returns the tooltip for the specified column.
7473 * @param {Number} col The column index
7476 getColumnTooltip : function(col){
7477 return this.config[col].tooltip;
7480 * Sets the tooltip for a column.
7481 * @param {Number} col The column index
7482 * @param {String} tooltip The new tooltip
7484 setColumnTooltip : function(col, tooltip){
7485 this.config[col].tooltip = tooltip;
7489 * Returns the dataIndex for the specified column.
7490 * @param {Number} col The column index
7493 getDataIndex : function(col){
7494 return this.config[col].dataIndex;
7498 * Sets the dataIndex for a column.
7499 * @param {Number} col The column index
7500 * @param {Number} dataIndex The new dataIndex
7502 setDataIndex : function(col, dataIndex){
7503 this.config[col].dataIndex = dataIndex;
7509 * Returns true if the cell is editable.
7510 * @param {Number} colIndex The column index
7511 * @param {Number} rowIndex The row index - this is nto actually used..?
7514 isCellEditable : function(colIndex, rowIndex){
7515 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7519 * Returns the editor defined for the cell/column.
7520 * return false or null to disable editing.
7521 * @param {Number} colIndex The column index
7522 * @param {Number} rowIndex The row index
7525 getCellEditor : function(colIndex, rowIndex){
7526 return this.config[colIndex].editor;
7530 * Sets if a column is editable.
7531 * @param {Number} col The column index
7532 * @param {Boolean} editable True if the column is editable
7534 setEditable : function(col, editable){
7535 this.config[col].editable = editable;
7540 * Returns true if the column is hidden.
7541 * @param {Number} colIndex The column index
7544 isHidden : function(colIndex){
7545 return this.config[colIndex].hidden;
7550 * Returns true if the column width cannot be changed
7552 isFixed : function(colIndex){
7553 return this.config[colIndex].fixed;
7557 * Returns true if the column can be resized
7560 isResizable : function(colIndex){
7561 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7564 * Sets if a column is hidden.
7565 * @param {Number} colIndex The column index
7566 * @param {Boolean} hidden True if the column is hidden
7568 setHidden : function(colIndex, hidden){
7569 this.config[colIndex].hidden = hidden;
7570 this.totalWidth = null;
7571 this.fireEvent("hiddenchange", this, colIndex, hidden);
7575 * Sets the editor for a column.
7576 * @param {Number} col The column index
7577 * @param {Object} editor The editor object
7579 setEditor : function(col, editor){
7580 this.config[col].editor = editor;
7584 Roo.grid.ColumnModel.defaultRenderer = function(value)
7586 if(typeof value == "object") {
7589 if(typeof value == "string" && value.length < 1){
7593 return String.format("{0}", value);
7596 // Alias for backwards compatibility
7597 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7600 * Ext JS Library 1.1.1
7601 * Copyright(c) 2006-2007, Ext JS, LLC.
7603 * Originally Released Under LGPL - original licence link has changed is not relivant.
7606 * <script type="text/javascript">
7610 * @class Roo.LoadMask
7611 * A simple utility class for generically masking elements while loading data. If the element being masked has
7612 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7613 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7614 * element's UpdateManager load indicator and will be destroyed after the initial load.
7616 * Create a new LoadMask
7617 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7618 * @param {Object} config The config object
7620 Roo.LoadMask = function(el, config){
7621 this.el = Roo.get(el);
7622 Roo.apply(this, config);
7624 this.store.on('beforeload', this.onBeforeLoad, this);
7625 this.store.on('load', this.onLoad, this);
7626 this.store.on('loadexception', this.onLoadException, this);
7627 this.removeMask = false;
7629 var um = this.el.getUpdateManager();
7630 um.showLoadIndicator = false; // disable the default indicator
7631 um.on('beforeupdate', this.onBeforeLoad, this);
7632 um.on('update', this.onLoad, this);
7633 um.on('failure', this.onLoad, this);
7634 this.removeMask = true;
7638 Roo.LoadMask.prototype = {
7640 * @cfg {Boolean} removeMask
7641 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7642 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7646 * The text to display in a centered loading message box (defaults to 'Loading...')
7650 * @cfg {String} msgCls
7651 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7653 msgCls : 'x-mask-loading',
7656 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7662 * Disables the mask to prevent it from being displayed
7664 disable : function(){
7665 this.disabled = true;
7669 * Enables the mask so that it can be displayed
7671 enable : function(){
7672 this.disabled = false;
7675 onLoadException : function()
7679 if (typeof(arguments[3]) != 'undefined') {
7680 Roo.MessageBox.alert("Error loading",arguments[3]);
7684 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7685 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7692 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7697 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7701 onBeforeLoad : function(){
7703 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7708 destroy : function(){
7710 this.store.un('beforeload', this.onBeforeLoad, this);
7711 this.store.un('load', this.onLoad, this);
7712 this.store.un('loadexception', this.onLoadException, this);
7714 var um = this.el.getUpdateManager();
7715 um.un('beforeupdate', this.onBeforeLoad, this);
7716 um.un('update', this.onLoad, this);
7717 um.un('failure', this.onLoad, this);
7728 * @class Roo.bootstrap.Table
7729 * @extends Roo.bootstrap.Component
7730 * Bootstrap Table class
7731 * @cfg {String} cls table class
7732 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7733 * @cfg {String} bgcolor Specifies the background color for a table
7734 * @cfg {Number} border Specifies whether the table cells should have borders or not
7735 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7736 * @cfg {Number} cellspacing Specifies the space between cells
7737 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7738 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7739 * @cfg {String} sortable Specifies that the table should be sortable
7740 * @cfg {String} summary Specifies a summary of the content of a table
7741 * @cfg {Number} width Specifies the width of a table
7742 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7744 * @cfg {boolean} striped Should the rows be alternative striped
7745 * @cfg {boolean} bordered Add borders to the table
7746 * @cfg {boolean} hover Add hover highlighting
7747 * @cfg {boolean} condensed Format condensed
7748 * @cfg {boolean} responsive Format condensed
7749 * @cfg {Boolean} loadMask (true|false) default false
7750 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7751 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7752 * @cfg {Boolean} rowSelection (true|false) default false
7753 * @cfg {Boolean} cellSelection (true|false) default false
7754 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7755 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7756 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7757 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7761 * Create a new Table
7762 * @param {Object} config The config object
7765 Roo.bootstrap.Table = function(config){
7766 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7771 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7772 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7773 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7774 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7776 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7778 this.sm.grid = this;
7779 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7780 this.sm = this.selModel;
7781 this.sm.xmodule = this.xmodule || false;
7784 if (this.cm && typeof(this.cm.config) == 'undefined') {
7785 this.colModel = new Roo.grid.ColumnModel(this.cm);
7786 this.cm = this.colModel;
7787 this.cm.xmodule = this.xmodule || false;
7790 this.store= Roo.factory(this.store, Roo.data);
7791 this.ds = this.store;
7792 this.ds.xmodule = this.xmodule || false;
7795 if (this.footer && this.store) {
7796 this.footer.dataSource = this.ds;
7797 this.footer = Roo.factory(this.footer);
7804 * Fires when a cell is clicked
7805 * @param {Roo.bootstrap.Table} this
7806 * @param {Roo.Element} el
7807 * @param {Number} rowIndex
7808 * @param {Number} columnIndex
7809 * @param {Roo.EventObject} e
7813 * @event celldblclick
7814 * Fires when a cell is double clicked
7815 * @param {Roo.bootstrap.Table} this
7816 * @param {Roo.Element} el
7817 * @param {Number} rowIndex
7818 * @param {Number} columnIndex
7819 * @param {Roo.EventObject} e
7821 "celldblclick" : true,
7824 * Fires when a row is clicked
7825 * @param {Roo.bootstrap.Table} this
7826 * @param {Roo.Element} el
7827 * @param {Number} rowIndex
7828 * @param {Roo.EventObject} e
7832 * @event rowdblclick
7833 * Fires when a row is double clicked
7834 * @param {Roo.bootstrap.Table} this
7835 * @param {Roo.Element} el
7836 * @param {Number} rowIndex
7837 * @param {Roo.EventObject} e
7839 "rowdblclick" : true,
7842 * Fires when a mouseover occur
7843 * @param {Roo.bootstrap.Table} this
7844 * @param {Roo.Element} el
7845 * @param {Number} rowIndex
7846 * @param {Number} columnIndex
7847 * @param {Roo.EventObject} e
7852 * Fires when a mouseout occur
7853 * @param {Roo.bootstrap.Table} this
7854 * @param {Roo.Element} el
7855 * @param {Number} rowIndex
7856 * @param {Number} columnIndex
7857 * @param {Roo.EventObject} e
7862 * Fires when a row is rendered, so you can change add a style to it.
7863 * @param {Roo.bootstrap.Table} this
7864 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7868 * @event rowsrendered
7869 * Fires when all the rows have been rendered
7870 * @param {Roo.bootstrap.Table} this
7872 'rowsrendered' : true,
7874 * @event contextmenu
7875 * The raw contextmenu event for the entire grid.
7876 * @param {Roo.EventObject} e
7878 "contextmenu" : true,
7880 * @event rowcontextmenu
7881 * Fires when a row is right clicked
7882 * @param {Roo.bootstrap.Table} this
7883 * @param {Number} rowIndex
7884 * @param {Roo.EventObject} e
7886 "rowcontextmenu" : true,
7888 * @event cellcontextmenu
7889 * Fires when a cell is right clicked
7890 * @param {Roo.bootstrap.Table} this
7891 * @param {Number} rowIndex
7892 * @param {Number} cellIndex
7893 * @param {Roo.EventObject} e
7895 "cellcontextmenu" : true,
7897 * @event headercontextmenu
7898 * Fires when a header is right clicked
7899 * @param {Roo.bootstrap.Table} this
7900 * @param {Number} columnIndex
7901 * @param {Roo.EventObject} e
7903 "headercontextmenu" : true
7907 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7933 rowSelection : false,
7934 cellSelection : false,
7937 // Roo.Element - the tbody
7939 // Roo.Element - thead element
7942 container: false, // used by gridpanel...
7948 auto_hide_footer : false,
7950 getAutoCreate : function()
7952 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7959 if (this.scrollBody) {
7960 cfg.cls += ' table-body-fixed';
7963 cfg.cls += ' table-striped';
7967 cfg.cls += ' table-hover';
7969 if (this.bordered) {
7970 cfg.cls += ' table-bordered';
7972 if (this.condensed) {
7973 cfg.cls += ' table-condensed';
7975 if (this.responsive) {
7976 cfg.cls += ' table-responsive';
7980 cfg.cls+= ' ' +this.cls;
7983 // this lot should be simplifed...
7996 ].forEach(function(k) {
8004 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8007 if(this.store || this.cm){
8008 if(this.headerShow){
8009 cfg.cn.push(this.renderHeader());
8012 cfg.cn.push(this.renderBody());
8014 if(this.footerShow){
8015 cfg.cn.push(this.renderFooter());
8017 // where does this come from?
8018 //cfg.cls+= ' TableGrid';
8021 return { cn : [ cfg ] };
8024 initEvents : function()
8026 if(!this.store || !this.cm){
8029 if (this.selModel) {
8030 this.selModel.initEvents();
8034 //Roo.log('initEvents with ds!!!!');
8036 this.mainBody = this.el.select('tbody', true).first();
8037 this.mainHead = this.el.select('thead', true).first();
8038 this.mainFoot = this.el.select('tfoot', true).first();
8044 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8045 e.on('click', _this.sort, _this);
8048 this.mainBody.on("click", this.onClick, this);
8049 this.mainBody.on("dblclick", this.onDblClick, this);
8051 // why is this done????? = it breaks dialogs??
8052 //this.parent().el.setStyle('position', 'relative');
8056 this.footer.parentId = this.id;
8057 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8060 this.el.select('tfoot tr td').first().addClass('hide');
8065 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8068 this.store.on('load', this.onLoad, this);
8069 this.store.on('beforeload', this.onBeforeLoad, this);
8070 this.store.on('update', this.onUpdate, this);
8071 this.store.on('add', this.onAdd, this);
8072 this.store.on("clear", this.clear, this);
8074 this.el.on("contextmenu", this.onContextMenu, this);
8076 this.mainBody.on('scroll', this.onBodyScroll, this);
8078 this.cm.on("headerchange", this.onHeaderChange, this);
8080 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8084 onContextMenu : function(e, t)
8086 this.processEvent("contextmenu", e);
8089 processEvent : function(name, e)
8091 if (name != 'touchstart' ) {
8092 this.fireEvent(name, e);
8095 var t = e.getTarget();
8097 var cell = Roo.get(t);
8103 if(cell.findParent('tfoot', false, true)){
8107 if(cell.findParent('thead', false, true)){
8109 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8110 cell = Roo.get(t).findParent('th', false, true);
8112 Roo.log("failed to find th in thead?");
8113 Roo.log(e.getTarget());
8118 var cellIndex = cell.dom.cellIndex;
8120 var ename = name == 'touchstart' ? 'click' : name;
8121 this.fireEvent("header" + ename, this, cellIndex, e);
8126 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8127 cell = Roo.get(t).findParent('td', false, true);
8129 Roo.log("failed to find th in tbody?");
8130 Roo.log(e.getTarget());
8135 var row = cell.findParent('tr', false, true);
8136 var cellIndex = cell.dom.cellIndex;
8137 var rowIndex = row.dom.rowIndex - 1;
8141 this.fireEvent("row" + name, this, rowIndex, e);
8145 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8151 onMouseover : function(e, el)
8153 var cell = Roo.get(el);
8159 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8160 cell = cell.findParent('td', false, true);
8163 var row = cell.findParent('tr', false, true);
8164 var cellIndex = cell.dom.cellIndex;
8165 var rowIndex = row.dom.rowIndex - 1; // start from 0
8167 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8171 onMouseout : function(e, el)
8173 var cell = Roo.get(el);
8179 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8180 cell = cell.findParent('td', false, true);
8183 var row = cell.findParent('tr', false, true);
8184 var cellIndex = cell.dom.cellIndex;
8185 var rowIndex = row.dom.rowIndex - 1; // start from 0
8187 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8191 onClick : function(e, el)
8193 var cell = Roo.get(el);
8195 if(!cell || (!this.cellSelection && !this.rowSelection)){
8199 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8200 cell = cell.findParent('td', false, true);
8203 if(!cell || typeof(cell) == 'undefined'){
8207 var row = cell.findParent('tr', false, true);
8209 if(!row || typeof(row) == 'undefined'){
8213 var cellIndex = cell.dom.cellIndex;
8214 var rowIndex = this.getRowIndex(row);
8216 // why??? - should these not be based on SelectionModel?
8217 if(this.cellSelection){
8218 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8221 if(this.rowSelection){
8222 this.fireEvent('rowclick', this, row, rowIndex, e);
8228 onDblClick : function(e,el)
8230 var cell = Roo.get(el);
8232 if(!cell || (!this.cellSelection && !this.rowSelection)){
8236 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8237 cell = cell.findParent('td', false, true);
8240 if(!cell || typeof(cell) == 'undefined'){
8244 var row = cell.findParent('tr', false, true);
8246 if(!row || typeof(row) == 'undefined'){
8250 var cellIndex = cell.dom.cellIndex;
8251 var rowIndex = this.getRowIndex(row);
8253 if(this.cellSelection){
8254 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8257 if(this.rowSelection){
8258 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8262 sort : function(e,el)
8264 var col = Roo.get(el);
8266 if(!col.hasClass('sortable')){
8270 var sort = col.attr('sort');
8273 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8277 this.store.sortInfo = {field : sort, direction : dir};
8280 Roo.log("calling footer first");
8281 this.footer.onClick('first');
8284 this.store.load({ params : { start : 0 } });
8288 renderHeader : function()
8296 this.totalWidth = 0;
8298 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8300 var config = cm.config[i];
8304 cls : 'x-hcol-' + i,
8306 html: cm.getColumnHeader(i)
8311 if(typeof(config.sortable) != 'undefined' && config.sortable){
8313 c.html = '<i class="glyphicon"></i>' + c.html;
8316 // could use BS4 hidden-..-down
8318 if(typeof(config.lgHeader) != 'undefined'){
8319 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8322 if(typeof(config.mdHeader) != 'undefined'){
8323 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8326 if(typeof(config.smHeader) != 'undefined'){
8327 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8330 if(typeof(config.xsHeader) != 'undefined'){
8331 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8338 if(typeof(config.tooltip) != 'undefined'){
8339 c.tooltip = config.tooltip;
8342 if(typeof(config.colspan) != 'undefined'){
8343 c.colspan = config.colspan;
8346 if(typeof(config.hidden) != 'undefined' && config.hidden){
8347 c.style += ' display:none;';
8350 if(typeof(config.dataIndex) != 'undefined'){
8351 c.sort = config.dataIndex;
8356 if(typeof(config.align) != 'undefined' && config.align.length){
8357 c.style += ' text-align:' + config.align + ';';
8360 if(typeof(config.width) != 'undefined'){
8361 c.style += ' width:' + config.width + 'px;';
8362 this.totalWidth += config.width;
8364 this.totalWidth += 100; // assume minimum of 100 per column?
8367 if(typeof(config.cls) != 'undefined'){
8368 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8371 ['xs','sm','md','lg'].map(function(size){
8373 if(typeof(config[size]) == 'undefined'){
8377 if (!config[size]) { // 0 = hidden
8378 // BS 4 '0' is treated as hide that column and below.
8379 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8383 c.cls += ' col-' + size + '-' + config[size] + (
8384 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8396 renderBody : function()
8406 colspan : this.cm.getColumnCount()
8416 renderFooter : function()
8426 colspan : this.cm.getColumnCount()
8440 // Roo.log('ds onload');
8445 var ds = this.store;
8447 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8448 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8449 if (_this.store.sortInfo) {
8451 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8452 e.select('i', true).addClass(['glyphicon-arrow-up']);
8455 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8456 e.select('i', true).addClass(['glyphicon-arrow-down']);
8461 var tbody = this.mainBody;
8463 if(ds.getCount() > 0){
8464 ds.data.each(function(d,rowIndex){
8465 var row = this.renderRow(cm, ds, rowIndex);
8467 tbody.createChild(row);
8471 if(row.cellObjects.length){
8472 Roo.each(row.cellObjects, function(r){
8473 _this.renderCellObject(r);
8480 var tfoot = this.el.select('tfoot', true).first();
8482 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8484 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8486 var total = this.ds.getTotalCount();
8488 if(this.footer.pageSize < total){
8489 this.mainFoot.show();
8493 Roo.each(this.el.select('tbody td', true).elements, function(e){
8494 e.on('mouseover', _this.onMouseover, _this);
8497 Roo.each(this.el.select('tbody td', true).elements, function(e){
8498 e.on('mouseout', _this.onMouseout, _this);
8500 this.fireEvent('rowsrendered', this);
8506 onUpdate : function(ds,record)
8508 this.refreshRow(record);
8512 onRemove : function(ds, record, index, isUpdate){
8513 if(isUpdate !== true){
8514 this.fireEvent("beforerowremoved", this, index, record);
8516 var bt = this.mainBody.dom;
8518 var rows = this.el.select('tbody > tr', true).elements;
8520 if(typeof(rows[index]) != 'undefined'){
8521 bt.removeChild(rows[index].dom);
8524 // if(bt.rows[index]){
8525 // bt.removeChild(bt.rows[index]);
8528 if(isUpdate !== true){
8529 //this.stripeRows(index);
8530 //this.syncRowHeights(index, index);
8532 this.fireEvent("rowremoved", this, index, record);
8536 onAdd : function(ds, records, rowIndex)
8538 //Roo.log('on Add called');
8539 // - note this does not handle multiple adding very well..
8540 var bt = this.mainBody.dom;
8541 for (var i =0 ; i < records.length;i++) {
8542 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8543 //Roo.log(records[i]);
8544 //Roo.log(this.store.getAt(rowIndex+i));
8545 this.insertRow(this.store, rowIndex + i, false);
8552 refreshRow : function(record){
8553 var ds = this.store, index;
8554 if(typeof record == 'number'){
8556 record = ds.getAt(index);
8558 index = ds.indexOf(record);
8560 return; // should not happen - but seems to
8563 this.insertRow(ds, index, true);
8565 this.onRemove(ds, record, index+1, true);
8567 //this.syncRowHeights(index, index);
8569 this.fireEvent("rowupdated", this, index, record);
8572 insertRow : function(dm, rowIndex, isUpdate){
8575 this.fireEvent("beforerowsinserted", this, rowIndex);
8577 //var s = this.getScrollState();
8578 var row = this.renderRow(this.cm, this.store, rowIndex);
8579 // insert before rowIndex..
8580 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8584 if(row.cellObjects.length){
8585 Roo.each(row.cellObjects, function(r){
8586 _this.renderCellObject(r);
8591 this.fireEvent("rowsinserted", this, rowIndex);
8592 //this.syncRowHeights(firstRow, lastRow);
8593 //this.stripeRows(firstRow);
8600 getRowDom : function(rowIndex)
8602 var rows = this.el.select('tbody > tr', true).elements;
8604 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8607 // returns the object tree for a tr..
8610 renderRow : function(cm, ds, rowIndex)
8612 var d = ds.getAt(rowIndex);
8616 cls : 'x-row-' + rowIndex,
8620 var cellObjects = [];
8622 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8623 var config = cm.config[i];
8625 var renderer = cm.getRenderer(i);
8629 if(typeof(renderer) !== 'undefined'){
8630 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8632 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8633 // and are rendered into the cells after the row is rendered - using the id for the element.
8635 if(typeof(value) === 'object'){
8645 rowIndex : rowIndex,
8650 this.fireEvent('rowclass', this, rowcfg);
8654 cls : rowcfg.rowClass + ' x-col-' + i,
8656 html: (typeof(value) === 'object') ? '' : value
8663 if(typeof(config.colspan) != 'undefined'){
8664 td.colspan = config.colspan;
8667 if(typeof(config.hidden) != 'undefined' && config.hidden){
8668 td.style += ' display:none;';
8671 if(typeof(config.align) != 'undefined' && config.align.length){
8672 td.style += ' text-align:' + config.align + ';';
8674 if(typeof(config.valign) != 'undefined' && config.valign.length){
8675 td.style += ' vertical-align:' + config.valign + ';';
8678 if(typeof(config.width) != 'undefined'){
8679 td.style += ' width:' + config.width + 'px;';
8682 if(typeof(config.cursor) != 'undefined'){
8683 td.style += ' cursor:' + config.cursor + ';';
8686 if(typeof(config.cls) != 'undefined'){
8687 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8690 ['xs','sm','md','lg'].map(function(size){
8692 if(typeof(config[size]) == 'undefined'){
8698 if (!config[size]) { // 0 = hidden
8699 // BS 4 '0' is treated as hide that column and below.
8700 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8704 td.cls += ' col-' + size + '-' + config[size] + (
8705 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8715 row.cellObjects = cellObjects;
8723 onBeforeLoad : function()
8732 this.el.select('tbody', true).first().dom.innerHTML = '';
8735 * Show or hide a row.
8736 * @param {Number} rowIndex to show or hide
8737 * @param {Boolean} state hide
8739 setRowVisibility : function(rowIndex, state)
8741 var bt = this.mainBody.dom;
8743 var rows = this.el.select('tbody > tr', true).elements;
8745 if(typeof(rows[rowIndex]) == 'undefined'){
8748 rows[rowIndex].dom.style.display = state ? '' : 'none';
8752 getSelectionModel : function(){
8754 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8756 return this.selModel;
8759 * Render the Roo.bootstrap object from renderder
8761 renderCellObject : function(r)
8765 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8767 var t = r.cfg.render(r.container);
8770 Roo.each(r.cfg.cn, function(c){
8772 container: t.getChildContainer(),
8775 _this.renderCellObject(child);
8780 getRowIndex : function(row)
8784 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8795 * Returns the grid's underlying element = used by panel.Grid
8796 * @return {Element} The element
8798 getGridEl : function(){
8802 * Forces a resize - used by panel.Grid
8803 * @return {Element} The element
8805 autoSize : function()
8807 //var ctr = Roo.get(this.container.dom.parentElement);
8808 var ctr = Roo.get(this.el.dom);
8810 var thd = this.getGridEl().select('thead',true).first();
8811 var tbd = this.getGridEl().select('tbody', true).first();
8812 var tfd = this.getGridEl().select('tfoot', true).first();
8814 var cw = ctr.getWidth();
8815 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8819 tbd.setWidth(ctr.getWidth());
8820 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8821 // this needs fixing for various usage - currently only hydra job advers I think..
8823 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8825 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8828 cw = Math.max(cw, this.totalWidth);
8829 this.getGridEl().select('tbody tr',true).setWidth(cw);
8831 // resize 'expandable coloumn?
8833 return; // we doe not have a view in this design..
8836 onBodyScroll: function()
8838 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8840 this.mainHead.setStyle({
8841 'position' : 'relative',
8842 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8848 var scrollHeight = this.mainBody.dom.scrollHeight;
8850 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8852 var height = this.mainBody.getHeight();
8854 if(scrollHeight - height == scrollTop) {
8856 var total = this.ds.getTotalCount();
8858 if(this.footer.cursor + this.footer.pageSize < total){
8860 this.footer.ds.load({
8862 start : this.footer.cursor + this.footer.pageSize,
8863 limit : this.footer.pageSize
8873 onHeaderChange : function()
8875 var header = this.renderHeader();
8876 var table = this.el.select('table', true).first();
8878 this.mainHead.remove();
8879 this.mainHead = table.createChild(header, this.mainBody, false);
8882 onHiddenChange : function(colModel, colIndex, hidden)
8884 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8885 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8887 this.CSS.updateRule(thSelector, "display", "");
8888 this.CSS.updateRule(tdSelector, "display", "");
8891 this.CSS.updateRule(thSelector, "display", "none");
8892 this.CSS.updateRule(tdSelector, "display", "none");
8895 this.onHeaderChange();
8899 setColumnWidth: function(col_index, width)
8901 // width = "md-2 xs-2..."
8902 if(!this.colModel.config[col_index]) {
8906 var w = width.split(" ");
8908 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8910 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8913 for(var j = 0; j < w.length; j++) {
8919 var size_cls = w[j].split("-");
8921 if(!Number.isInteger(size_cls[1] * 1)) {
8925 if(!this.colModel.config[col_index][size_cls[0]]) {
8929 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8933 h_row[0].classList.replace(
8934 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8935 "col-"+size_cls[0]+"-"+size_cls[1]
8938 for(var i = 0; i < rows.length; i++) {
8940 var size_cls = w[j].split("-");
8942 if(!Number.isInteger(size_cls[1] * 1)) {
8946 if(!this.colModel.config[col_index][size_cls[0]]) {
8950 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8954 rows[i].classList.replace(
8955 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8956 "col-"+size_cls[0]+"-"+size_cls[1]
8960 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8975 * @class Roo.bootstrap.TableCell
8976 * @extends Roo.bootstrap.Component
8977 * Bootstrap TableCell class
8978 * @cfg {String} html cell contain text
8979 * @cfg {String} cls cell class
8980 * @cfg {String} tag cell tag (td|th) default td
8981 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8982 * @cfg {String} align Aligns the content in a cell
8983 * @cfg {String} axis Categorizes cells
8984 * @cfg {String} bgcolor Specifies the background color of a cell
8985 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8986 * @cfg {Number} colspan Specifies the number of columns a cell should span
8987 * @cfg {String} headers Specifies one or more header cells a cell is related to
8988 * @cfg {Number} height Sets the height of a cell
8989 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8990 * @cfg {Number} rowspan Sets the number of rows a cell should span
8991 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8992 * @cfg {String} valign Vertical aligns the content in a cell
8993 * @cfg {Number} width Specifies the width of a cell
8996 * Create a new TableCell
8997 * @param {Object} config The config object
9000 Roo.bootstrap.TableCell = function(config){
9001 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9004 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9024 getAutoCreate : function(){
9025 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9045 cfg.align=this.align
9051 cfg.bgcolor=this.bgcolor
9054 cfg.charoff=this.charoff
9057 cfg.colspan=this.colspan
9060 cfg.headers=this.headers
9063 cfg.height=this.height
9066 cfg.nowrap=this.nowrap
9069 cfg.rowspan=this.rowspan
9072 cfg.scope=this.scope
9075 cfg.valign=this.valign
9078 cfg.width=this.width
9097 * @class Roo.bootstrap.TableRow
9098 * @extends Roo.bootstrap.Component
9099 * Bootstrap TableRow class
9100 * @cfg {String} cls row class
9101 * @cfg {String} align Aligns the content in a table row
9102 * @cfg {String} bgcolor Specifies a background color for a table row
9103 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9104 * @cfg {String} valign Vertical aligns the content in a table row
9107 * Create a new TableRow
9108 * @param {Object} config The config object
9111 Roo.bootstrap.TableRow = function(config){
9112 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9115 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9123 getAutoCreate : function(){
9124 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9134 cfg.align = this.align;
9137 cfg.bgcolor = this.bgcolor;
9140 cfg.charoff = this.charoff;
9143 cfg.valign = this.valign;
9161 * @class Roo.bootstrap.TableBody
9162 * @extends Roo.bootstrap.Component
9163 * Bootstrap TableBody class
9164 * @cfg {String} cls element class
9165 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9166 * @cfg {String} align Aligns the content inside the element
9167 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9168 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9171 * Create a new TableBody
9172 * @param {Object} config The config object
9175 Roo.bootstrap.TableBody = function(config){
9176 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9179 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9187 getAutoCreate : function(){
9188 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9202 cfg.align = this.align;
9205 cfg.charoff = this.charoff;
9208 cfg.valign = this.valign;
9215 // initEvents : function()
9222 // this.store = Roo.factory(this.store, Roo.data);
9223 // this.store.on('load', this.onLoad, this);
9225 // this.store.load();
9229 // onLoad: function ()
9231 // this.fireEvent('load', this);
9241 * Ext JS Library 1.1.1
9242 * Copyright(c) 2006-2007, Ext JS, LLC.
9244 * Originally Released Under LGPL - original licence link has changed is not relivant.
9247 * <script type="text/javascript">
9250 // as we use this in bootstrap.
9251 Roo.namespace('Roo.form');
9253 * @class Roo.form.Action
9254 * Internal Class used to handle form actions
9256 * @param {Roo.form.BasicForm} el The form element or its id
9257 * @param {Object} config Configuration options
9262 // define the action interface
9263 Roo.form.Action = function(form, options){
9265 this.options = options || {};
9268 * Client Validation Failed
9271 Roo.form.Action.CLIENT_INVALID = 'client';
9273 * Server Validation Failed
9276 Roo.form.Action.SERVER_INVALID = 'server';
9278 * Connect to Server Failed
9281 Roo.form.Action.CONNECT_FAILURE = 'connect';
9283 * Reading Data from Server Failed
9286 Roo.form.Action.LOAD_FAILURE = 'load';
9288 Roo.form.Action.prototype = {
9290 failureType : undefined,
9291 response : undefined,
9295 run : function(options){
9300 success : function(response){
9305 handleResponse : function(response){
9309 // default connection failure
9310 failure : function(response){
9312 this.response = response;
9313 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9314 this.form.afterAction(this, false);
9317 processResponse : function(response){
9318 this.response = response;
9319 if(!response.responseText){
9322 this.result = this.handleResponse(response);
9326 // utility functions used internally
9327 getUrl : function(appendParams){
9328 var url = this.options.url || this.form.url || this.form.el.dom.action;
9330 var p = this.getParams();
9332 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9338 getMethod : function(){
9339 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9342 getParams : function(){
9343 var bp = this.form.baseParams;
9344 var p = this.options.params;
9346 if(typeof p == "object"){
9347 p = Roo.urlEncode(Roo.applyIf(p, bp));
9348 }else if(typeof p == 'string' && bp){
9349 p += '&' + Roo.urlEncode(bp);
9352 p = Roo.urlEncode(bp);
9357 createCallback : function(){
9359 success: this.success,
9360 failure: this.failure,
9362 timeout: (this.form.timeout*1000),
9363 upload: this.form.fileUpload ? this.success : undefined
9368 Roo.form.Action.Submit = function(form, options){
9369 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9372 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9375 haveProgress : false,
9376 uploadComplete : false,
9378 // uploadProgress indicator.
9379 uploadProgress : function()
9381 if (!this.form.progressUrl) {
9385 if (!this.haveProgress) {
9386 Roo.MessageBox.progress("Uploading", "Uploading");
9388 if (this.uploadComplete) {
9389 Roo.MessageBox.hide();
9393 this.haveProgress = true;
9395 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9397 var c = new Roo.data.Connection();
9399 url : this.form.progressUrl,
9404 success : function(req){
9405 //console.log(data);
9409 rdata = Roo.decode(req.responseText)
9411 Roo.log("Invalid data from server..");
9415 if (!rdata || !rdata.success) {
9417 Roo.MessageBox.alert(Roo.encode(rdata));
9420 var data = rdata.data;
9422 if (this.uploadComplete) {
9423 Roo.MessageBox.hide();
9428 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9429 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9432 this.uploadProgress.defer(2000,this);
9435 failure: function(data) {
9436 Roo.log('progress url failed ');
9447 // run get Values on the form, so it syncs any secondary forms.
9448 this.form.getValues();
9450 var o = this.options;
9451 var method = this.getMethod();
9452 var isPost = method == 'POST';
9453 if(o.clientValidation === false || this.form.isValid()){
9455 if (this.form.progressUrl) {
9456 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9457 (new Date() * 1) + '' + Math.random());
9462 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9463 form:this.form.el.dom,
9464 url:this.getUrl(!isPost),
9466 params:isPost ? this.getParams() : null,
9467 isUpload: this.form.fileUpload,
9468 formData : this.form.formData
9471 this.uploadProgress();
9473 }else if (o.clientValidation !== false){ // client validation failed
9474 this.failureType = Roo.form.Action.CLIENT_INVALID;
9475 this.form.afterAction(this, false);
9479 success : function(response)
9481 this.uploadComplete= true;
9482 if (this.haveProgress) {
9483 Roo.MessageBox.hide();
9487 var result = this.processResponse(response);
9488 if(result === true || result.success){
9489 this.form.afterAction(this, true);
9493 this.form.markInvalid(result.errors);
9494 this.failureType = Roo.form.Action.SERVER_INVALID;
9496 this.form.afterAction(this, false);
9498 failure : function(response)
9500 this.uploadComplete= true;
9501 if (this.haveProgress) {
9502 Roo.MessageBox.hide();
9505 this.response = response;
9506 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9507 this.form.afterAction(this, false);
9510 handleResponse : function(response){
9511 if(this.form.errorReader){
9512 var rs = this.form.errorReader.read(response);
9515 for(var i = 0, len = rs.records.length; i < len; i++) {
9516 var r = rs.records[i];
9520 if(errors.length < 1){
9524 success : rs.success,
9530 ret = Roo.decode(response.responseText);
9534 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9544 Roo.form.Action.Load = function(form, options){
9545 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9546 this.reader = this.form.reader;
9549 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9554 Roo.Ajax.request(Roo.apply(
9555 this.createCallback(), {
9556 method:this.getMethod(),
9557 url:this.getUrl(false),
9558 params:this.getParams()
9562 success : function(response){
9564 var result = this.processResponse(response);
9565 if(result === true || !result.success || !result.data){
9566 this.failureType = Roo.form.Action.LOAD_FAILURE;
9567 this.form.afterAction(this, false);
9570 this.form.clearInvalid();
9571 this.form.setValues(result.data);
9572 this.form.afterAction(this, true);
9575 handleResponse : function(response){
9576 if(this.form.reader){
9577 var rs = this.form.reader.read(response);
9578 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9580 success : rs.success,
9584 return Roo.decode(response.responseText);
9588 Roo.form.Action.ACTION_TYPES = {
9589 'load' : Roo.form.Action.Load,
9590 'submit' : Roo.form.Action.Submit
9599 * @class Roo.bootstrap.Form
9600 * @extends Roo.bootstrap.Component
9601 * Bootstrap Form class
9602 * @cfg {String} method GET | POST (default POST)
9603 * @cfg {String} labelAlign top | left (default top)
9604 * @cfg {String} align left | right - for navbars
9605 * @cfg {Boolean} loadMask load mask when submit (default true)
9610 * @param {Object} config The config object
9614 Roo.bootstrap.Form = function(config){
9616 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9618 Roo.bootstrap.Form.popover.apply();
9622 * @event clientvalidation
9623 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9624 * @param {Form} this
9625 * @param {Boolean} valid true if the form has passed client-side validation
9627 clientvalidation: true,
9629 * @event beforeaction
9630 * Fires before any action is performed. Return false to cancel the action.
9631 * @param {Form} this
9632 * @param {Action} action The action to be performed
9636 * @event actionfailed
9637 * Fires when an action fails.
9638 * @param {Form} this
9639 * @param {Action} action The action that failed
9641 actionfailed : true,
9643 * @event actioncomplete
9644 * Fires when an action is completed.
9645 * @param {Form} this
9646 * @param {Action} action The action that completed
9648 actioncomplete : true
9652 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9655 * @cfg {String} method
9656 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9661 * The URL to use for form actions if one isn't supplied in the action options.
9664 * @cfg {Boolean} fileUpload
9665 * Set to true if this form is a file upload.
9669 * @cfg {Object} baseParams
9670 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9674 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9678 * @cfg {Sting} align (left|right) for navbar forms
9683 activeAction : null,
9686 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9687 * element by passing it or its id or mask the form itself by passing in true.
9690 waitMsgTarget : false,
9695 * @cfg {Boolean} errorMask (true|false) default false
9700 * @cfg {Number} maskOffset Default 100
9705 * @cfg {Boolean} maskBody
9709 getAutoCreate : function(){
9713 method : this.method || 'POST',
9714 id : this.id || Roo.id(),
9717 if (this.parent().xtype.match(/^Nav/)) {
9718 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9722 if (this.labelAlign == 'left' ) {
9723 cfg.cls += ' form-horizontal';
9729 initEvents : function()
9731 this.el.on('submit', this.onSubmit, this);
9732 // this was added as random key presses on the form where triggering form submit.
9733 this.el.on('keypress', function(e) {
9734 if (e.getCharCode() != 13) {
9737 // we might need to allow it for textareas.. and some other items.
9738 // check e.getTarget().
9740 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9744 Roo.log("keypress blocked");
9752 onSubmit : function(e){
9757 * Returns true if client-side validation on the form is successful.
9760 isValid : function(){
9761 var items = this.getItems();
9765 items.each(function(f){
9771 Roo.log('invalid field: ' + f.name);
9775 if(!target && f.el.isVisible(true)){
9781 if(this.errorMask && !valid){
9782 Roo.bootstrap.Form.popover.mask(this, target);
9789 * Returns true if any fields in this form have changed since their original load.
9792 isDirty : function(){
9794 var items = this.getItems();
9795 items.each(function(f){
9805 * Performs a predefined action (submit or load) or custom actions you define on this form.
9806 * @param {String} actionName The name of the action type
9807 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9808 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9809 * accept other config options):
9811 Property Type Description
9812 ---------------- --------------- ----------------------------------------------------------------------------------
9813 url String The url for the action (defaults to the form's url)
9814 method String The form method to use (defaults to the form's method, or POST if not defined)
9815 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9816 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9817 validate the form on the client (defaults to false)
9819 * @return {BasicForm} this
9821 doAction : function(action, options){
9822 if(typeof action == 'string'){
9823 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9825 if(this.fireEvent('beforeaction', this, action) !== false){
9826 this.beforeAction(action);
9827 action.run.defer(100, action);
9833 beforeAction : function(action){
9834 var o = action.options;
9839 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9841 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9844 // not really supported yet.. ??
9846 //if(this.waitMsgTarget === true){
9847 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9848 //}else if(this.waitMsgTarget){
9849 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9850 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9852 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9858 afterAction : function(action, success){
9859 this.activeAction = null;
9860 var o = action.options;
9865 Roo.get(document.body).unmask();
9871 //if(this.waitMsgTarget === true){
9872 // this.el.unmask();
9873 //}else if(this.waitMsgTarget){
9874 // this.waitMsgTarget.unmask();
9876 // Roo.MessageBox.updateProgress(1);
9877 // Roo.MessageBox.hide();
9884 Roo.callback(o.success, o.scope, [this, action]);
9885 this.fireEvent('actioncomplete', this, action);
9889 // failure condition..
9890 // we have a scenario where updates need confirming.
9891 // eg. if a locking scenario exists..
9892 // we look for { errors : { needs_confirm : true }} in the response.
9894 (typeof(action.result) != 'undefined') &&
9895 (typeof(action.result.errors) != 'undefined') &&
9896 (typeof(action.result.errors.needs_confirm) != 'undefined')
9899 Roo.log("not supported yet");
9902 Roo.MessageBox.confirm(
9903 "Change requires confirmation",
9904 action.result.errorMsg,
9909 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9919 Roo.callback(o.failure, o.scope, [this, action]);
9920 // show an error message if no failed handler is set..
9921 if (!this.hasListener('actionfailed')) {
9922 Roo.log("need to add dialog support");
9924 Roo.MessageBox.alert("Error",
9925 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9926 action.result.errorMsg :
9927 "Saving Failed, please check your entries or try again"
9932 this.fireEvent('actionfailed', this, action);
9937 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9938 * @param {String} id The value to search for
9941 findField : function(id){
9942 var items = this.getItems();
9943 var field = items.get(id);
9945 items.each(function(f){
9946 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9953 return field || null;
9956 * Mark fields in this form invalid in bulk.
9957 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9958 * @return {BasicForm} this
9960 markInvalid : function(errors){
9961 if(errors instanceof Array){
9962 for(var i = 0, len = errors.length; i < len; i++){
9963 var fieldError = errors[i];
9964 var f = this.findField(fieldError.id);
9966 f.markInvalid(fieldError.msg);
9972 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9973 field.markInvalid(errors[id]);
9977 //Roo.each(this.childForms || [], function (f) {
9978 // f.markInvalid(errors);
9985 * Set values for fields in this form in bulk.
9986 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9987 * @return {BasicForm} this
9989 setValues : function(values){
9990 if(values instanceof Array){ // array of objects
9991 for(var i = 0, len = values.length; i < len; i++){
9993 var f = this.findField(v.id);
9995 f.setValue(v.value);
9996 if(this.trackResetOnLoad){
9997 f.originalValue = f.getValue();
10001 }else{ // object hash
10004 if(typeof values[id] != 'function' && (field = this.findField(id))){
10006 if (field.setFromData &&
10007 field.valueField &&
10008 field.displayField &&
10009 // combos' with local stores can
10010 // be queried via setValue()
10011 // to set their value..
10012 (field.store && !field.store.isLocal)
10016 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10017 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10018 field.setFromData(sd);
10020 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10022 field.setFromData(values);
10025 field.setValue(values[id]);
10029 if(this.trackResetOnLoad){
10030 field.originalValue = field.getValue();
10036 //Roo.each(this.childForms || [], function (f) {
10037 // f.setValues(values);
10044 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10045 * they are returned as an array.
10046 * @param {Boolean} asString
10049 getValues : function(asString){
10050 //if (this.childForms) {
10051 // copy values from the child forms
10052 // Roo.each(this.childForms, function (f) {
10053 // this.setValues(f.getValues());
10059 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10060 if(asString === true){
10063 return Roo.urlDecode(fs);
10067 * Returns the fields in this form as an object with key/value pairs.
10068 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10071 getFieldValues : function(with_hidden)
10073 var items = this.getItems();
10075 items.each(function(f){
10077 if (!f.getName()) {
10081 var v = f.getValue();
10083 if (f.inputType =='radio') {
10084 if (typeof(ret[f.getName()]) == 'undefined') {
10085 ret[f.getName()] = ''; // empty..
10088 if (!f.el.dom.checked) {
10092 v = f.el.dom.value;
10096 if(f.xtype == 'MoneyField'){
10097 ret[f.currencyName] = f.getCurrency();
10100 // not sure if this supported any more..
10101 if ((typeof(v) == 'object') && f.getRawValue) {
10102 v = f.getRawValue() ; // dates..
10104 // combo boxes where name != hiddenName...
10105 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10106 ret[f.name] = f.getRawValue();
10108 ret[f.getName()] = v;
10115 * Clears all invalid messages in this form.
10116 * @return {BasicForm} this
10118 clearInvalid : function(){
10119 var items = this.getItems();
10121 items.each(function(f){
10129 * Resets this form.
10130 * @return {BasicForm} this
10132 reset : function(){
10133 var items = this.getItems();
10134 items.each(function(f){
10138 Roo.each(this.childForms || [], function (f) {
10146 getItems : function()
10148 var r=new Roo.util.MixedCollection(false, function(o){
10149 return o.id || (o.id = Roo.id());
10151 var iter = function(el) {
10158 Roo.each(el.items,function(e) {
10167 hideFields : function(items)
10169 Roo.each(items, function(i){
10171 var f = this.findField(i);
10182 showFields : function(items)
10184 Roo.each(items, function(i){
10186 var f = this.findField(i);
10199 Roo.apply(Roo.bootstrap.Form, {
10215 intervalID : false,
10221 if(this.isApplied){
10226 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10227 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10228 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10229 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10232 this.maskEl.top.enableDisplayMode("block");
10233 this.maskEl.left.enableDisplayMode("block");
10234 this.maskEl.bottom.enableDisplayMode("block");
10235 this.maskEl.right.enableDisplayMode("block");
10237 this.toolTip = new Roo.bootstrap.Tooltip({
10238 cls : 'roo-form-error-popover',
10240 'left' : ['r-l', [-2,0], 'right'],
10241 'right' : ['l-r', [2,0], 'left'],
10242 'bottom' : ['tl-bl', [0,2], 'top'],
10243 'top' : [ 'bl-tl', [0,-2], 'bottom']
10247 this.toolTip.render(Roo.get(document.body));
10249 this.toolTip.el.enableDisplayMode("block");
10251 Roo.get(document.body).on('click', function(){
10255 Roo.get(document.body).on('touchstart', function(){
10259 this.isApplied = true
10262 mask : function(form, target)
10266 this.target = target;
10268 if(!this.form.errorMask || !target.el){
10272 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10274 Roo.log(scrollable);
10276 var ot = this.target.el.calcOffsetsTo(scrollable);
10278 var scrollTo = ot[1] - this.form.maskOffset;
10280 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10282 scrollable.scrollTo('top', scrollTo);
10284 var box = this.target.el.getBox();
10286 var zIndex = Roo.bootstrap.Modal.zIndex++;
10289 this.maskEl.top.setStyle('position', 'absolute');
10290 this.maskEl.top.setStyle('z-index', zIndex);
10291 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10292 this.maskEl.top.setLeft(0);
10293 this.maskEl.top.setTop(0);
10294 this.maskEl.top.show();
10296 this.maskEl.left.setStyle('position', 'absolute');
10297 this.maskEl.left.setStyle('z-index', zIndex);
10298 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10299 this.maskEl.left.setLeft(0);
10300 this.maskEl.left.setTop(box.y - this.padding);
10301 this.maskEl.left.show();
10303 this.maskEl.bottom.setStyle('position', 'absolute');
10304 this.maskEl.bottom.setStyle('z-index', zIndex);
10305 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10306 this.maskEl.bottom.setLeft(0);
10307 this.maskEl.bottom.setTop(box.bottom + this.padding);
10308 this.maskEl.bottom.show();
10310 this.maskEl.right.setStyle('position', 'absolute');
10311 this.maskEl.right.setStyle('z-index', zIndex);
10312 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10313 this.maskEl.right.setLeft(box.right + this.padding);
10314 this.maskEl.right.setTop(box.y - this.padding);
10315 this.maskEl.right.show();
10317 this.toolTip.bindEl = this.target.el;
10319 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10321 var tip = this.target.blankText;
10323 if(this.target.getValue() !== '' ) {
10325 if (this.target.invalidText.length) {
10326 tip = this.target.invalidText;
10327 } else if (this.target.regexText.length){
10328 tip = this.target.regexText;
10332 this.toolTip.show(tip);
10334 this.intervalID = window.setInterval(function() {
10335 Roo.bootstrap.Form.popover.unmask();
10338 window.onwheel = function(){ return false;};
10340 (function(){ this.isMasked = true; }).defer(500, this);
10344 unmask : function()
10346 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10350 this.maskEl.top.setStyle('position', 'absolute');
10351 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10352 this.maskEl.top.hide();
10354 this.maskEl.left.setStyle('position', 'absolute');
10355 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10356 this.maskEl.left.hide();
10358 this.maskEl.bottom.setStyle('position', 'absolute');
10359 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10360 this.maskEl.bottom.hide();
10362 this.maskEl.right.setStyle('position', 'absolute');
10363 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10364 this.maskEl.right.hide();
10366 this.toolTip.hide();
10368 this.toolTip.el.hide();
10370 window.onwheel = function(){ return true;};
10372 if(this.intervalID){
10373 window.clearInterval(this.intervalID);
10374 this.intervalID = false;
10377 this.isMasked = false;
10387 * Ext JS Library 1.1.1
10388 * Copyright(c) 2006-2007, Ext JS, LLC.
10390 * Originally Released Under LGPL - original licence link has changed is not relivant.
10393 * <script type="text/javascript">
10396 * @class Roo.form.VTypes
10397 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10400 Roo.form.VTypes = function(){
10401 // closure these in so they are only created once.
10402 var alpha = /^[a-zA-Z_]+$/;
10403 var alphanum = /^[a-zA-Z0-9_]+$/;
10404 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10405 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10407 // All these messages and functions are configurable
10410 * The function used to validate email addresses
10411 * @param {String} value The email address
10413 'email' : function(v){
10414 return email.test(v);
10417 * The error text to display when the email validation function returns false
10420 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10422 * The keystroke filter mask to be applied on email input
10425 'emailMask' : /[a-z0-9_\.\-@]/i,
10428 * The function used to validate URLs
10429 * @param {String} value The URL
10431 'url' : function(v){
10432 return url.test(v);
10435 * The error text to display when the url validation function returns false
10438 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10441 * The function used to validate alpha values
10442 * @param {String} value The value
10444 'alpha' : function(v){
10445 return alpha.test(v);
10448 * The error text to display when the alpha validation function returns false
10451 'alphaText' : 'This field should only contain letters and _',
10453 * The keystroke filter mask to be applied on alpha input
10456 'alphaMask' : /[a-z_]/i,
10459 * The function used to validate alphanumeric values
10460 * @param {String} value The value
10462 'alphanum' : function(v){
10463 return alphanum.test(v);
10466 * The error text to display when the alphanumeric validation function returns false
10469 'alphanumText' : 'This field should only contain letters, numbers and _',
10471 * The keystroke filter mask to be applied on alphanumeric input
10474 'alphanumMask' : /[a-z0-9_]/i
10484 * @class Roo.bootstrap.Input
10485 * @extends Roo.bootstrap.Component
10486 * Bootstrap Input class
10487 * @cfg {Boolean} disabled is it disabled
10488 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10489 * @cfg {String} name name of the input
10490 * @cfg {string} fieldLabel - the label associated
10491 * @cfg {string} placeholder - placeholder to put in text.
10492 * @cfg {string} before - input group add on before
10493 * @cfg {string} after - input group add on after
10494 * @cfg {string} size - (lg|sm) or leave empty..
10495 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10496 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10497 * @cfg {Number} md colspan out of 12 for computer-sized screens
10498 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10499 * @cfg {string} value default value of the input
10500 * @cfg {Number} labelWidth set the width of label
10501 * @cfg {Number} labellg set the width of label (1-12)
10502 * @cfg {Number} labelmd set the width of label (1-12)
10503 * @cfg {Number} labelsm set the width of label (1-12)
10504 * @cfg {Number} labelxs set the width of label (1-12)
10505 * @cfg {String} labelAlign (top|left)
10506 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10507 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10508 * @cfg {String} indicatorpos (left|right) default left
10509 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10510 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10511 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10513 * @cfg {String} align (left|center|right) Default left
10514 * @cfg {Boolean} forceFeedback (true|false) Default false
10517 * Create a new Input
10518 * @param {Object} config The config object
10521 Roo.bootstrap.Input = function(config){
10523 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10528 * Fires when this field receives input focus.
10529 * @param {Roo.form.Field} this
10534 * Fires when this field loses input focus.
10535 * @param {Roo.form.Field} this
10539 * @event specialkey
10540 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10541 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10542 * @param {Roo.form.Field} this
10543 * @param {Roo.EventObject} e The event object
10548 * Fires just before the field blurs if the field value has changed.
10549 * @param {Roo.form.Field} this
10550 * @param {Mixed} newValue The new value
10551 * @param {Mixed} oldValue The original value
10556 * Fires after the field has been marked as invalid.
10557 * @param {Roo.form.Field} this
10558 * @param {String} msg The validation message
10563 * Fires after the field has been validated with no errors.
10564 * @param {Roo.form.Field} this
10569 * Fires after the key up
10570 * @param {Roo.form.Field} this
10571 * @param {Roo.EventObject} e The event Object
10576 * Fires after the user pastes into input
10577 * @param {Roo.form.Field} this
10578 * @param {Roo.EventObject} e The event Object
10584 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10586 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10587 automatic validation (defaults to "keyup").
10589 validationEvent : "keyup",
10591 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10593 validateOnBlur : true,
10595 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10597 validationDelay : 250,
10599 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10601 focusClass : "x-form-focus", // not needed???
10605 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10607 invalidClass : "has-warning",
10610 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10612 validClass : "has-success",
10615 * @cfg {Boolean} hasFeedback (true|false) default true
10617 hasFeedback : true,
10620 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10622 invalidFeedbackClass : "glyphicon-warning-sign",
10625 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10627 validFeedbackClass : "glyphicon-ok",
10630 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10632 selectOnFocus : false,
10635 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10639 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10644 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10646 disableKeyFilter : false,
10649 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10653 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10657 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10659 blankText : "Please complete this mandatory field",
10662 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10666 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10668 maxLength : Number.MAX_VALUE,
10670 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10672 minLengthText : "The minimum length for this field is {0}",
10674 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10676 maxLengthText : "The maximum length for this field is {0}",
10680 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10681 * If available, this function will be called only after the basic validators all return true, and will be passed the
10682 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10686 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10687 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10688 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10692 * @cfg {String} regexText -- Depricated - use Invalid Text
10697 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10703 autocomplete: false,
10707 inputType : 'text',
10710 placeholder: false,
10715 preventMark: false,
10716 isFormField : true,
10719 labelAlign : false,
10722 formatedValue : false,
10723 forceFeedback : false,
10725 indicatorpos : 'left',
10735 parentLabelAlign : function()
10738 while (parent.parent()) {
10739 parent = parent.parent();
10740 if (typeof(parent.labelAlign) !='undefined') {
10741 return parent.labelAlign;
10748 getAutoCreate : function()
10750 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10756 if(this.inputType != 'hidden'){
10757 cfg.cls = 'form-group' //input-group
10763 type : this.inputType,
10764 value : this.value,
10765 cls : 'form-control',
10766 placeholder : this.placeholder || '',
10767 autocomplete : this.autocomplete || 'new-password'
10769 if (this.inputType == 'file') {
10770 input.style = 'overflow:hidden'; // why not in CSS?
10773 if(this.capture.length){
10774 input.capture = this.capture;
10777 if(this.accept.length){
10778 input.accept = this.accept + "/*";
10782 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10785 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10786 input.maxLength = this.maxLength;
10789 if (this.disabled) {
10790 input.disabled=true;
10793 if (this.readOnly) {
10794 input.readonly=true;
10798 input.name = this.name;
10802 input.cls += ' input-' + this.size;
10806 ['xs','sm','md','lg'].map(function(size){
10807 if (settings[size]) {
10808 cfg.cls += ' col-' + size + '-' + settings[size];
10812 var inputblock = input;
10816 cls: 'glyphicon form-control-feedback'
10819 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10822 cls : 'has-feedback',
10830 if (this.before || this.after) {
10833 cls : 'input-group',
10837 if (this.before && typeof(this.before) == 'string') {
10839 inputblock.cn.push({
10841 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10845 if (this.before && typeof(this.before) == 'object') {
10846 this.before = Roo.factory(this.before);
10848 inputblock.cn.push({
10850 cls : 'roo-input-before input-group-prepend input-group-' +
10851 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10855 inputblock.cn.push(input);
10857 if (this.after && typeof(this.after) == 'string') {
10858 inputblock.cn.push({
10860 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10864 if (this.after && typeof(this.after) == 'object') {
10865 this.after = Roo.factory(this.after);
10867 inputblock.cn.push({
10869 cls : 'roo-input-after input-group-append input-group-' +
10870 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10874 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10875 inputblock.cls += ' has-feedback';
10876 inputblock.cn.push(feedback);
10881 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10882 tooltip : 'This field is required'
10884 if (this.allowBlank ) {
10885 indicator.style = this.allowBlank ? ' display:none' : '';
10887 if (align ==='left' && this.fieldLabel.length) {
10889 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10896 cls : 'control-label col-form-label',
10897 html : this.fieldLabel
10908 var labelCfg = cfg.cn[1];
10909 var contentCfg = cfg.cn[2];
10911 if(this.indicatorpos == 'right'){
10916 cls : 'control-label col-form-label',
10920 html : this.fieldLabel
10934 labelCfg = cfg.cn[0];
10935 contentCfg = cfg.cn[1];
10939 if(this.labelWidth > 12){
10940 labelCfg.style = "width: " + this.labelWidth + 'px';
10943 if(this.labelWidth < 13 && this.labelmd == 0){
10944 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
10947 if(this.labellg > 0){
10948 labelCfg.cls += ' col-lg-' + this.labellg;
10949 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10952 if(this.labelmd > 0){
10953 labelCfg.cls += ' col-md-' + this.labelmd;
10954 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10957 if(this.labelsm > 0){
10958 labelCfg.cls += ' col-sm-' + this.labelsm;
10959 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10962 if(this.labelxs > 0){
10963 labelCfg.cls += ' col-xs-' + this.labelxs;
10964 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10968 } else if ( this.fieldLabel.length) {
10975 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10976 tooltip : 'This field is required',
10977 style : this.allowBlank ? ' display:none' : ''
10981 //cls : 'input-group-addon',
10982 html : this.fieldLabel
10990 if(this.indicatorpos == 'right'){
10995 //cls : 'input-group-addon',
10996 html : this.fieldLabel
11001 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11002 tooltip : 'This field is required',
11003 style : this.allowBlank ? ' display:none' : ''
11023 if (this.parentType === 'Navbar' && this.parent().bar) {
11024 cfg.cls += ' navbar-form';
11027 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11028 // on BS4 we do this only if not form
11029 cfg.cls += ' navbar-form';
11037 * return the real input element.
11039 inputEl: function ()
11041 return this.el.select('input.form-control',true).first();
11044 tooltipEl : function()
11046 return this.inputEl();
11049 indicatorEl : function()
11051 if (Roo.bootstrap.version == 4) {
11052 return false; // not enabled in v4 yet.
11055 var indicator = this.el.select('i.roo-required-indicator',true).first();
11065 setDisabled : function(v)
11067 var i = this.inputEl().dom;
11069 i.removeAttribute('disabled');
11073 i.setAttribute('disabled','true');
11075 initEvents : function()
11078 this.inputEl().on("keydown" , this.fireKey, this);
11079 this.inputEl().on("focus", this.onFocus, this);
11080 this.inputEl().on("blur", this.onBlur, this);
11082 this.inputEl().relayEvent('keyup', this);
11083 this.inputEl().relayEvent('paste', this);
11085 this.indicator = this.indicatorEl();
11087 if(this.indicator){
11088 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11091 // reference to original value for reset
11092 this.originalValue = this.getValue();
11093 //Roo.form.TextField.superclass.initEvents.call(this);
11094 if(this.validationEvent == 'keyup'){
11095 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11096 this.inputEl().on('keyup', this.filterValidation, this);
11098 else if(this.validationEvent !== false){
11099 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11102 if(this.selectOnFocus){
11103 this.on("focus", this.preFocus, this);
11106 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11107 this.inputEl().on("keypress", this.filterKeys, this);
11109 this.inputEl().relayEvent('keypress', this);
11112 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11113 this.el.on("click", this.autoSize, this);
11116 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11117 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11120 if (typeof(this.before) == 'object') {
11121 this.before.render(this.el.select('.roo-input-before',true).first());
11123 if (typeof(this.after) == 'object') {
11124 this.after.render(this.el.select('.roo-input-after',true).first());
11127 this.inputEl().on('change', this.onChange, this);
11130 filterValidation : function(e){
11131 if(!e.isNavKeyPress()){
11132 this.validationTask.delay(this.validationDelay);
11136 * Validates the field value
11137 * @return {Boolean} True if the value is valid, else false
11139 validate : function(){
11140 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11141 if(this.disabled || this.validateValue(this.getRawValue())){
11146 this.markInvalid();
11152 * Validates a value according to the field's validation rules and marks the field as invalid
11153 * if the validation fails
11154 * @param {Mixed} value The value to validate
11155 * @return {Boolean} True if the value is valid, else false
11157 validateValue : function(value)
11159 if(this.getVisibilityEl().hasClass('hidden')){
11163 if(value.length < 1) { // if it's blank
11164 if(this.allowBlank){
11170 if(value.length < this.minLength){
11173 if(value.length > this.maxLength){
11177 var vt = Roo.form.VTypes;
11178 if(!vt[this.vtype](value, this)){
11182 if(typeof this.validator == "function"){
11183 var msg = this.validator(value);
11187 if (typeof(msg) == 'string') {
11188 this.invalidText = msg;
11192 if(this.regex && !this.regex.test(value)){
11200 fireKey : function(e){
11201 //Roo.log('field ' + e.getKey());
11202 if(e.isNavKeyPress()){
11203 this.fireEvent("specialkey", this, e);
11206 focus : function (selectText){
11208 this.inputEl().focus();
11209 if(selectText === true){
11210 this.inputEl().dom.select();
11216 onFocus : function(){
11217 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11218 // this.el.addClass(this.focusClass);
11220 if(!this.hasFocus){
11221 this.hasFocus = true;
11222 this.startValue = this.getValue();
11223 this.fireEvent("focus", this);
11227 beforeBlur : Roo.emptyFn,
11231 onBlur : function(){
11233 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11234 //this.el.removeClass(this.focusClass);
11236 this.hasFocus = false;
11237 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11240 var v = this.getValue();
11241 if(String(v) !== String(this.startValue)){
11242 this.fireEvent('change', this, v, this.startValue);
11244 this.fireEvent("blur", this);
11247 onChange : function(e)
11249 var v = this.getValue();
11250 if(String(v) !== String(this.startValue)){
11251 this.fireEvent('change', this, v, this.startValue);
11257 * Resets the current field value to the originally loaded value and clears any validation messages
11259 reset : function(){
11260 this.setValue(this.originalValue);
11264 * Returns the name of the field
11265 * @return {Mixed} name The name field
11267 getName: function(){
11271 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11272 * @return {Mixed} value The field value
11274 getValue : function(){
11276 var v = this.inputEl().getValue();
11281 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11282 * @return {Mixed} value The field value
11284 getRawValue : function(){
11285 var v = this.inputEl().getValue();
11291 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11292 * @param {Mixed} value The value to set
11294 setRawValue : function(v){
11295 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11298 selectText : function(start, end){
11299 var v = this.getRawValue();
11301 start = start === undefined ? 0 : start;
11302 end = end === undefined ? v.length : end;
11303 var d = this.inputEl().dom;
11304 if(d.setSelectionRange){
11305 d.setSelectionRange(start, end);
11306 }else if(d.createTextRange){
11307 var range = d.createTextRange();
11308 range.moveStart("character", start);
11309 range.moveEnd("character", v.length-end);
11316 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11317 * @param {Mixed} value The value to set
11319 setValue : function(v){
11322 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11328 processValue : function(value){
11329 if(this.stripCharsRe){
11330 var newValue = value.replace(this.stripCharsRe, '');
11331 if(newValue !== value){
11332 this.setRawValue(newValue);
11339 preFocus : function(){
11341 if(this.selectOnFocus){
11342 this.inputEl().dom.select();
11345 filterKeys : function(e){
11346 var k = e.getKey();
11347 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11350 var c = e.getCharCode(), cc = String.fromCharCode(c);
11351 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11354 if(!this.maskRe.test(cc)){
11359 * Clear any invalid styles/messages for this field
11361 clearInvalid : function(){
11363 if(!this.el || this.preventMark){ // not rendered
11368 this.el.removeClass([this.invalidClass, 'is-invalid']);
11370 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11372 var feedback = this.el.select('.form-control-feedback', true).first();
11375 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11380 if(this.indicator){
11381 this.indicator.removeClass('visible');
11382 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11385 this.fireEvent('valid', this);
11389 * Mark this field as valid
11391 markValid : function()
11393 if(!this.el || this.preventMark){ // not rendered...
11397 this.el.removeClass([this.invalidClass, this.validClass]);
11398 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11400 var feedback = this.el.select('.form-control-feedback', true).first();
11403 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11406 if(this.indicator){
11407 this.indicator.removeClass('visible');
11408 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11416 if(this.allowBlank && !this.getRawValue().length){
11419 if (Roo.bootstrap.version == 3) {
11420 this.el.addClass(this.validClass);
11422 this.inputEl().addClass('is-valid');
11425 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11427 var feedback = this.el.select('.form-control-feedback', true).first();
11430 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11431 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11436 this.fireEvent('valid', this);
11440 * Mark this field as invalid
11441 * @param {String} msg The validation message
11443 markInvalid : function(msg)
11445 if(!this.el || this.preventMark){ // not rendered
11449 this.el.removeClass([this.invalidClass, this.validClass]);
11450 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11452 var feedback = this.el.select('.form-control-feedback', true).first();
11455 this.el.select('.form-control-feedback', true).first().removeClass(
11456 [this.invalidFeedbackClass, this.validFeedbackClass]);
11463 if(this.allowBlank && !this.getRawValue().length){
11467 if(this.indicator){
11468 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11469 this.indicator.addClass('visible');
11471 if (Roo.bootstrap.version == 3) {
11472 this.el.addClass(this.invalidClass);
11474 this.inputEl().addClass('is-invalid');
11479 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11481 var feedback = this.el.select('.form-control-feedback', true).first();
11484 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11486 if(this.getValue().length || this.forceFeedback){
11487 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11494 this.fireEvent('invalid', this, msg);
11497 SafariOnKeyDown : function(event)
11499 // this is a workaround for a password hang bug on chrome/ webkit.
11500 if (this.inputEl().dom.type != 'password') {
11504 var isSelectAll = false;
11506 if(this.inputEl().dom.selectionEnd > 0){
11507 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11509 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11510 event.preventDefault();
11515 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11517 event.preventDefault();
11518 // this is very hacky as keydown always get's upper case.
11520 var cc = String.fromCharCode(event.getCharCode());
11521 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11525 adjustWidth : function(tag, w){
11526 tag = tag.toLowerCase();
11527 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11528 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11529 if(tag == 'input'){
11532 if(tag == 'textarea'){
11535 }else if(Roo.isOpera){
11536 if(tag == 'input'){
11539 if(tag == 'textarea'){
11547 setFieldLabel : function(v)
11549 if(!this.rendered){
11553 if(this.indicatorEl()){
11554 var ar = this.el.select('label > span',true);
11556 if (ar.elements.length) {
11557 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11558 this.fieldLabel = v;
11562 var br = this.el.select('label',true);
11564 if(br.elements.length) {
11565 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11566 this.fieldLabel = v;
11570 Roo.log('Cannot Found any of label > span || label in input');
11574 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11575 this.fieldLabel = v;
11590 * @class Roo.bootstrap.TextArea
11591 * @extends Roo.bootstrap.Input
11592 * Bootstrap TextArea class
11593 * @cfg {Number} cols Specifies the visible width of a text area
11594 * @cfg {Number} rows Specifies the visible number of lines in a text area
11595 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11596 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11597 * @cfg {string} html text
11600 * Create a new TextArea
11601 * @param {Object} config The config object
11604 Roo.bootstrap.TextArea = function(config){
11605 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11609 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11619 getAutoCreate : function(){
11621 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11627 if(this.inputType != 'hidden'){
11628 cfg.cls = 'form-group' //input-group
11636 value : this.value || '',
11637 html: this.html || '',
11638 cls : 'form-control',
11639 placeholder : this.placeholder || ''
11643 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11644 input.maxLength = this.maxLength;
11648 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11652 input.cols = this.cols;
11655 if (this.readOnly) {
11656 input.readonly = true;
11660 input.name = this.name;
11664 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11668 ['xs','sm','md','lg'].map(function(size){
11669 if (settings[size]) {
11670 cfg.cls += ' col-' + size + '-' + settings[size];
11674 var inputblock = input;
11676 if(this.hasFeedback && !this.allowBlank){
11680 cls: 'glyphicon form-control-feedback'
11684 cls : 'has-feedback',
11693 if (this.before || this.after) {
11696 cls : 'input-group',
11700 inputblock.cn.push({
11702 cls : 'input-group-addon',
11707 inputblock.cn.push(input);
11709 if(this.hasFeedback && !this.allowBlank){
11710 inputblock.cls += ' has-feedback';
11711 inputblock.cn.push(feedback);
11715 inputblock.cn.push({
11717 cls : 'input-group-addon',
11724 if (align ==='left' && this.fieldLabel.length) {
11729 cls : 'control-label',
11730 html : this.fieldLabel
11741 if(this.labelWidth > 12){
11742 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11745 if(this.labelWidth < 13 && this.labelmd == 0){
11746 this.labelmd = this.labelWidth;
11749 if(this.labellg > 0){
11750 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11751 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11754 if(this.labelmd > 0){
11755 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11756 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11759 if(this.labelsm > 0){
11760 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11761 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11764 if(this.labelxs > 0){
11765 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11766 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11769 } else if ( this.fieldLabel.length) {
11774 //cls : 'input-group-addon',
11775 html : this.fieldLabel
11793 if (this.disabled) {
11794 input.disabled=true;
11801 * return the real textarea element.
11803 inputEl: function ()
11805 return this.el.select('textarea.form-control',true).first();
11809 * Clear any invalid styles/messages for this field
11811 clearInvalid : function()
11814 if(!this.el || this.preventMark){ // not rendered
11818 var label = this.el.select('label', true).first();
11819 var icon = this.el.select('i.fa-star', true).first();
11824 this.el.removeClass( this.validClass);
11825 this.inputEl().removeClass('is-invalid');
11827 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11829 var feedback = this.el.select('.form-control-feedback', true).first();
11832 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11837 this.fireEvent('valid', this);
11841 * Mark this field as valid
11843 markValid : function()
11845 if(!this.el || this.preventMark){ // not rendered
11849 this.el.removeClass([this.invalidClass, this.validClass]);
11850 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11852 var feedback = this.el.select('.form-control-feedback', true).first();
11855 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11858 if(this.disabled || this.allowBlank){
11862 var label = this.el.select('label', true).first();
11863 var icon = this.el.select('i.fa-star', true).first();
11868 if (Roo.bootstrap.version == 3) {
11869 this.el.addClass(this.validClass);
11871 this.inputEl().addClass('is-valid');
11875 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11877 var feedback = this.el.select('.form-control-feedback', true).first();
11880 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11881 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11886 this.fireEvent('valid', this);
11890 * Mark this field as invalid
11891 * @param {String} msg The validation message
11893 markInvalid : function(msg)
11895 if(!this.el || this.preventMark){ // not rendered
11899 this.el.removeClass([this.invalidClass, this.validClass]);
11900 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11902 var feedback = this.el.select('.form-control-feedback', true).first();
11905 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11908 if(this.disabled || this.allowBlank){
11912 var label = this.el.select('label', true).first();
11913 var icon = this.el.select('i.fa-star', true).first();
11915 if(!this.getValue().length && label && !icon){
11916 this.el.createChild({
11918 cls : 'text-danger fa fa-lg fa-star',
11919 tooltip : 'This field is required',
11920 style : 'margin-right:5px;'
11924 if (Roo.bootstrap.version == 3) {
11925 this.el.addClass(this.invalidClass);
11927 this.inputEl().addClass('is-invalid');
11930 // fixme ... this may be depricated need to test..
11931 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11933 var feedback = this.el.select('.form-control-feedback', true).first();
11936 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11938 if(this.getValue().length || this.forceFeedback){
11939 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11946 this.fireEvent('invalid', this, msg);
11954 * trigger field - base class for combo..
11959 * @class Roo.bootstrap.TriggerField
11960 * @extends Roo.bootstrap.Input
11961 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11962 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11963 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11964 * for which you can provide a custom implementation. For example:
11966 var trigger = new Roo.bootstrap.TriggerField();
11967 trigger.onTriggerClick = myTriggerFn;
11968 trigger.applyTo('my-field');
11971 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11972 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11973 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11974 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11975 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11978 * Create a new TriggerField.
11979 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11980 * to the base TextField)
11982 Roo.bootstrap.TriggerField = function(config){
11983 this.mimicing = false;
11984 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11987 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11989 * @cfg {String} triggerClass A CSS class to apply to the trigger
11992 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11997 * @cfg {Boolean} removable (true|false) special filter default false
12001 /** @cfg {Boolean} grow @hide */
12002 /** @cfg {Number} growMin @hide */
12003 /** @cfg {Number} growMax @hide */
12009 autoSize: Roo.emptyFn,
12013 deferHeight : true,
12016 actionMode : 'wrap',
12021 getAutoCreate : function(){
12023 var align = this.labelAlign || this.parentLabelAlign();
12028 cls: 'form-group' //input-group
12035 type : this.inputType,
12036 cls : 'form-control',
12037 autocomplete: 'new-password',
12038 placeholder : this.placeholder || ''
12042 input.name = this.name;
12045 input.cls += ' input-' + this.size;
12048 if (this.disabled) {
12049 input.disabled=true;
12052 var inputblock = input;
12054 if(this.hasFeedback && !this.allowBlank){
12058 cls: 'glyphicon form-control-feedback'
12061 if(this.removable && !this.editable ){
12063 cls : 'has-feedback',
12069 cls : 'roo-combo-removable-btn close'
12076 cls : 'has-feedback',
12085 if(this.removable && !this.editable ){
12087 cls : 'roo-removable',
12093 cls : 'roo-combo-removable-btn close'
12100 if (this.before || this.after) {
12103 cls : 'input-group',
12107 inputblock.cn.push({
12109 cls : 'input-group-addon input-group-prepend input-group-text',
12114 inputblock.cn.push(input);
12116 if(this.hasFeedback && !this.allowBlank){
12117 inputblock.cls += ' has-feedback';
12118 inputblock.cn.push(feedback);
12122 inputblock.cn.push({
12124 cls : 'input-group-addon input-group-append input-group-text',
12133 var ibwrap = inputblock;
12138 cls: 'roo-select2-choices',
12142 cls: 'roo-select2-search-field',
12154 cls: 'roo-select2-container input-group',
12159 cls: 'form-hidden-field'
12165 if(!this.multiple && this.showToggleBtn){
12171 if (this.caret != false) {
12174 cls: 'fa fa-' + this.caret
12181 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12183 Roo.bootstrap.version == 3 ? caret : '',
12186 cls: 'combobox-clear',
12200 combobox.cls += ' roo-select2-container-multi';
12204 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12205 tooltip : 'This field is required'
12207 if (Roo.bootstrap.version == 4) {
12210 style : 'display:none'
12215 if (align ==='left' && this.fieldLabel.length) {
12217 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12224 cls : 'control-label',
12225 html : this.fieldLabel
12237 var labelCfg = cfg.cn[1];
12238 var contentCfg = cfg.cn[2];
12240 if(this.indicatorpos == 'right'){
12245 cls : 'control-label',
12249 html : this.fieldLabel
12263 labelCfg = cfg.cn[0];
12264 contentCfg = cfg.cn[1];
12267 if(this.labelWidth > 12){
12268 labelCfg.style = "width: " + this.labelWidth + 'px';
12271 if(this.labelWidth < 13 && this.labelmd == 0){
12272 this.labelmd = this.labelWidth;
12275 if(this.labellg > 0){
12276 labelCfg.cls += ' col-lg-' + this.labellg;
12277 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12280 if(this.labelmd > 0){
12281 labelCfg.cls += ' col-md-' + this.labelmd;
12282 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12285 if(this.labelsm > 0){
12286 labelCfg.cls += ' col-sm-' + this.labelsm;
12287 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12290 if(this.labelxs > 0){
12291 labelCfg.cls += ' col-xs-' + this.labelxs;
12292 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12295 } else if ( this.fieldLabel.length) {
12296 // Roo.log(" label");
12301 //cls : 'input-group-addon',
12302 html : this.fieldLabel
12310 if(this.indicatorpos == 'right'){
12318 html : this.fieldLabel
12332 // Roo.log(" no label && no align");
12339 ['xs','sm','md','lg'].map(function(size){
12340 if (settings[size]) {
12341 cfg.cls += ' col-' + size + '-' + settings[size];
12352 onResize : function(w, h){
12353 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12354 // if(typeof w == 'number'){
12355 // var x = w - this.trigger.getWidth();
12356 // this.inputEl().setWidth(this.adjustWidth('input', x));
12357 // this.trigger.setStyle('left', x+'px');
12362 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12365 getResizeEl : function(){
12366 return this.inputEl();
12370 getPositionEl : function(){
12371 return this.inputEl();
12375 alignErrorIcon : function(){
12376 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12380 initEvents : function(){
12384 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12385 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12386 if(!this.multiple && this.showToggleBtn){
12387 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12388 if(this.hideTrigger){
12389 this.trigger.setDisplayed(false);
12391 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12395 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12398 if(this.removable && !this.editable && !this.tickable){
12399 var close = this.closeTriggerEl();
12402 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12403 close.on('click', this.removeBtnClick, this, close);
12407 //this.trigger.addClassOnOver('x-form-trigger-over');
12408 //this.trigger.addClassOnClick('x-form-trigger-click');
12411 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12415 closeTriggerEl : function()
12417 var close = this.el.select('.roo-combo-removable-btn', true).first();
12418 return close ? close : false;
12421 removeBtnClick : function(e, h, el)
12423 e.preventDefault();
12425 if(this.fireEvent("remove", this) !== false){
12427 this.fireEvent("afterremove", this)
12431 createList : function()
12433 this.list = Roo.get(document.body).createChild({
12434 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12435 cls: 'typeahead typeahead-long dropdown-menu shadow',
12436 style: 'display:none'
12439 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12444 initTrigger : function(){
12449 onDestroy : function(){
12451 this.trigger.removeAllListeners();
12452 // this.trigger.remove();
12455 // this.wrap.remove();
12457 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12461 onFocus : function(){
12462 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12464 if(!this.mimicing){
12465 this.wrap.addClass('x-trigger-wrap-focus');
12466 this.mimicing = true;
12467 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12468 if(this.monitorTab){
12469 this.el.on("keydown", this.checkTab, this);
12476 checkTab : function(e){
12477 if(e.getKey() == e.TAB){
12478 this.triggerBlur();
12483 onBlur : function(){
12488 mimicBlur : function(e, t){
12490 if(!this.wrap.contains(t) && this.validateBlur()){
12491 this.triggerBlur();
12497 triggerBlur : function(){
12498 this.mimicing = false;
12499 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12500 if(this.monitorTab){
12501 this.el.un("keydown", this.checkTab, this);
12503 //this.wrap.removeClass('x-trigger-wrap-focus');
12504 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12508 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12509 validateBlur : function(e, t){
12514 onDisable : function(){
12515 this.inputEl().dom.disabled = true;
12516 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12518 // this.wrap.addClass('x-item-disabled');
12523 onEnable : function(){
12524 this.inputEl().dom.disabled = false;
12525 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12527 // this.el.removeClass('x-item-disabled');
12532 onShow : function(){
12533 var ae = this.getActionEl();
12536 ae.dom.style.display = '';
12537 ae.dom.style.visibility = 'visible';
12543 onHide : function(){
12544 var ae = this.getActionEl();
12545 ae.dom.style.display = 'none';
12549 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12550 * by an implementing function.
12552 * @param {EventObject} e
12554 onTriggerClick : Roo.emptyFn
12562 * @class Roo.bootstrap.CardUploader
12563 * @extends Roo.bootstrap.Button
12564 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12565 * @cfg {Number} errorTimeout default 3000
12566 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12567 * @cfg {Array} html The button text.
12571 * Create a new CardUploader
12572 * @param {Object} config The config object
12575 Roo.bootstrap.CardUploader = function(config){
12579 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12582 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12590 * When a image is clicked on - and needs to display a slideshow or similar..
12591 * @param {Roo.bootstrap.Card} this
12592 * @param {Object} The image information data
12598 * When a the download link is clicked
12599 * @param {Roo.bootstrap.Card} this
12600 * @param {Object} The image information data contains
12607 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12610 errorTimeout : 3000,
12614 fileCollection : false,
12617 getAutoCreate : function()
12621 cls :'form-group' ,
12626 //cls : 'input-group-addon',
12627 html : this.fieldLabel
12635 value : this.value,
12636 cls : 'd-none form-control'
12641 multiple : 'multiple',
12643 cls : 'd-none roo-card-upload-selector'
12647 cls : 'roo-card-uploader-button-container w-100 mb-2'
12650 cls : 'card-columns roo-card-uploader-container'
12660 getChildContainer : function() /// what children are added to.
12662 return this.containerEl;
12665 getButtonContainer : function() /// what children are added to.
12667 return this.el.select(".roo-card-uploader-button-container").first();
12670 initEvents : function()
12673 Roo.bootstrap.Input.prototype.initEvents.call(this);
12677 xns: Roo.bootstrap,
12680 container_method : 'getButtonContainer' ,
12681 html : this.html, // fix changable?
12684 'click' : function(btn, e) {
12693 this.urlAPI = (window.createObjectURL && window) ||
12694 (window.URL && URL.revokeObjectURL && URL) ||
12695 (window.webkitURL && webkitURL);
12700 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12702 this.selectorEl.on('change', this.onFileSelected, this);
12705 this.images.forEach(function(img) {
12708 this.images = false;
12710 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12716 onClick : function(e)
12718 e.preventDefault();
12720 this.selectorEl.dom.click();
12724 onFileSelected : function(e)
12726 e.preventDefault();
12728 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12732 Roo.each(this.selectorEl.dom.files, function(file){
12733 this.addFile(file);
12742 addFile : function(file)
12745 if(typeof(file) === 'string'){
12746 throw "Add file by name?"; // should not happen
12750 if(!file || !this.urlAPI){
12760 var url = _this.urlAPI.createObjectURL( file);
12763 id : Roo.bootstrap.CardUploader.ID--,
12764 is_uploaded : false,
12768 mimetype : file.type,
12776 * addCard - add an Attachment to the uploader
12777 * @param data - the data about the image to upload
12781 title : "Title of file",
12782 is_uploaded : false,
12783 src : "http://.....",
12784 srcfile : { the File upload object },
12785 mimetype : file.type,
12788 .. any other data...
12794 addCard : function (data)
12796 // hidden input element?
12797 // if the file is not an image...
12798 //then we need to use something other that and header_image
12803 xns : Roo.bootstrap,
12804 xtype : 'CardFooter',
12807 xns : Roo.bootstrap,
12813 xns : Roo.bootstrap,
12815 html : String.format("<small>{0}</small>", data.title),
12816 cls : 'col-10 text-left',
12821 click : function() {
12823 t.fireEvent( "download", t, data );
12829 xns : Roo.bootstrap,
12831 style: 'max-height: 28px; ',
12837 click : function() {
12838 t.removeCard(data.id)
12850 var cn = this.addxtype(
12853 xns : Roo.bootstrap,
12856 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12857 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12858 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12863 initEvents : function() {
12864 Roo.bootstrap.Card.prototype.initEvents.call(this);
12866 this.imgEl = this.el.select('.card-img-top').first();
12868 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12869 this.imgEl.set({ 'pointer' : 'cursor' });
12872 this.getCardFooter().addClass('p-1');
12879 // dont' really need ot update items.
12880 // this.items.push(cn);
12881 this.fileCollection.add(cn);
12883 if (!data.srcfile) {
12884 this.updateInput();
12889 var reader = new FileReader();
12890 reader.addEventListener("load", function() {
12891 data.srcdata = reader.result;
12894 reader.readAsDataURL(data.srcfile);
12899 removeCard : function(id)
12902 var card = this.fileCollection.get(id);
12903 card.data.is_deleted = 1;
12904 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12905 //this.fileCollection.remove(card);
12906 //this.items = this.items.filter(function(e) { return e != card });
12907 // dont' really need ot update items.
12908 card.el.dom.parentNode.removeChild(card.el.dom);
12909 this.updateInput();
12915 this.fileCollection.each(function(card) {
12916 if (card.el.dom && card.el.dom.parentNode) {
12917 card.el.dom.parentNode.removeChild(card.el.dom);
12920 this.fileCollection.clear();
12921 this.updateInput();
12924 updateInput : function()
12927 this.fileCollection.each(function(e) {
12931 this.inputEl().dom.value = JSON.stringify(data);
12941 Roo.bootstrap.CardUploader.ID = -1;/*
12943 * Ext JS Library 1.1.1
12944 * Copyright(c) 2006-2007, Ext JS, LLC.
12946 * Originally Released Under LGPL - original licence link has changed is not relivant.
12949 * <script type="text/javascript">
12954 * @class Roo.data.SortTypes
12956 * Defines the default sorting (casting?) comparison functions used when sorting data.
12958 Roo.data.SortTypes = {
12960 * Default sort that does nothing
12961 * @param {Mixed} s The value being converted
12962 * @return {Mixed} The comparison value
12964 none : function(s){
12969 * The regular expression used to strip tags
12973 stripTagsRE : /<\/?[^>]+>/gi,
12976 * Strips all HTML tags to sort on text only
12977 * @param {Mixed} s The value being converted
12978 * @return {String} The comparison value
12980 asText : function(s){
12981 return String(s).replace(this.stripTagsRE, "");
12985 * Strips all HTML tags to sort on text only - Case insensitive
12986 * @param {Mixed} s The value being converted
12987 * @return {String} The comparison value
12989 asUCText : function(s){
12990 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12994 * Case insensitive string
12995 * @param {Mixed} s The value being converted
12996 * @return {String} The comparison value
12998 asUCString : function(s) {
12999 return String(s).toUpperCase();
13004 * @param {Mixed} s The value being converted
13005 * @return {Number} The comparison value
13007 asDate : function(s) {
13011 if(s instanceof Date){
13012 return s.getTime();
13014 return Date.parse(String(s));
13019 * @param {Mixed} s The value being converted
13020 * @return {Float} The comparison value
13022 asFloat : function(s) {
13023 var val = parseFloat(String(s).replace(/,/g, ""));
13032 * @param {Mixed} s The value being converted
13033 * @return {Number} The comparison value
13035 asInt : function(s) {
13036 var val = parseInt(String(s).replace(/,/g, ""));
13044 * Ext JS Library 1.1.1
13045 * Copyright(c) 2006-2007, Ext JS, LLC.
13047 * Originally Released Under LGPL - original licence link has changed is not relivant.
13050 * <script type="text/javascript">
13054 * @class Roo.data.Record
13055 * Instances of this class encapsulate both record <em>definition</em> information, and record
13056 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13057 * to access Records cached in an {@link Roo.data.Store} object.<br>
13059 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13060 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13063 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13065 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13066 * {@link #create}. The parameters are the same.
13067 * @param {Array} data An associative Array of data values keyed by the field name.
13068 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13069 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13070 * not specified an integer id is generated.
13072 Roo.data.Record = function(data, id){
13073 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13078 * Generate a constructor for a specific record layout.
13079 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13080 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13081 * Each field definition object may contain the following properties: <ul>
13082 * <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,
13083 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13084 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13085 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13086 * is being used, then this is a string containing the javascript expression to reference the data relative to
13087 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13088 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13089 * this may be omitted.</p></li>
13090 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13091 * <ul><li>auto (Default, implies no conversion)</li>
13096 * <li>date</li></ul></p></li>
13097 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13098 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13099 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13100 * by the Reader into an object that will be stored in the Record. It is passed the
13101 * following parameters:<ul>
13102 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13104 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13106 * <br>usage:<br><pre><code>
13107 var TopicRecord = Roo.data.Record.create(
13108 {name: 'title', mapping: 'topic_title'},
13109 {name: 'author', mapping: 'username'},
13110 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13111 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13112 {name: 'lastPoster', mapping: 'user2'},
13113 {name: 'excerpt', mapping: 'post_text'}
13116 var myNewRecord = new TopicRecord({
13117 title: 'Do my job please',
13120 lastPost: new Date(),
13121 lastPoster: 'Animal',
13122 excerpt: 'No way dude!'
13124 myStore.add(myNewRecord);
13129 Roo.data.Record.create = function(o){
13130 var f = function(){
13131 f.superclass.constructor.apply(this, arguments);
13133 Roo.extend(f, Roo.data.Record);
13134 var p = f.prototype;
13135 p.fields = new Roo.util.MixedCollection(false, function(field){
13138 for(var i = 0, len = o.length; i < len; i++){
13139 p.fields.add(new Roo.data.Field(o[i]));
13141 f.getField = function(name){
13142 return p.fields.get(name);
13147 Roo.data.Record.AUTO_ID = 1000;
13148 Roo.data.Record.EDIT = 'edit';
13149 Roo.data.Record.REJECT = 'reject';
13150 Roo.data.Record.COMMIT = 'commit';
13152 Roo.data.Record.prototype = {
13154 * Readonly flag - true if this record has been modified.
13163 join : function(store){
13164 this.store = store;
13168 * Set the named field to the specified value.
13169 * @param {String} name The name of the field to set.
13170 * @param {Object} value The value to set the field to.
13172 set : function(name, value){
13173 if(this.data[name] == value){
13177 if(!this.modified){
13178 this.modified = {};
13180 if(typeof this.modified[name] == 'undefined'){
13181 this.modified[name] = this.data[name];
13183 this.data[name] = value;
13184 if(!this.editing && this.store){
13185 this.store.afterEdit(this);
13190 * Get the value of the named field.
13191 * @param {String} name The name of the field to get the value of.
13192 * @return {Object} The value of the field.
13194 get : function(name){
13195 return this.data[name];
13199 beginEdit : function(){
13200 this.editing = true;
13201 this.modified = {};
13205 cancelEdit : function(){
13206 this.editing = false;
13207 delete this.modified;
13211 endEdit : function(){
13212 this.editing = false;
13213 if(this.dirty && this.store){
13214 this.store.afterEdit(this);
13219 * Usually called by the {@link Roo.data.Store} which owns the Record.
13220 * Rejects all changes made to the Record since either creation, or the last commit operation.
13221 * Modified fields are reverted to their original values.
13223 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13224 * of reject operations.
13226 reject : function(){
13227 var m = this.modified;
13229 if(typeof m[n] != "function"){
13230 this.data[n] = m[n];
13233 this.dirty = false;
13234 delete this.modified;
13235 this.editing = false;
13237 this.store.afterReject(this);
13242 * Usually called by the {@link Roo.data.Store} which owns the Record.
13243 * Commits all changes made to the Record since either creation, or the last commit operation.
13245 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13246 * of commit operations.
13248 commit : function(){
13249 this.dirty = false;
13250 delete this.modified;
13251 this.editing = false;
13253 this.store.afterCommit(this);
13258 hasError : function(){
13259 return this.error != null;
13263 clearError : function(){
13268 * Creates a copy of this record.
13269 * @param {String} id (optional) A new record id if you don't want to use this record's id
13272 copy : function(newId) {
13273 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13277 * Ext JS Library 1.1.1
13278 * Copyright(c) 2006-2007, Ext JS, LLC.
13280 * Originally Released Under LGPL - original licence link has changed is not relivant.
13283 * <script type="text/javascript">
13289 * @class Roo.data.Store
13290 * @extends Roo.util.Observable
13291 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13292 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13294 * 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
13295 * has no knowledge of the format of the data returned by the Proxy.<br>
13297 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13298 * instances from the data object. These records are cached and made available through accessor functions.
13300 * Creates a new Store.
13301 * @param {Object} config A config object containing the objects needed for the Store to access data,
13302 * and read the data into Records.
13304 Roo.data.Store = function(config){
13305 this.data = new Roo.util.MixedCollection(false);
13306 this.data.getKey = function(o){
13309 this.baseParams = {};
13311 this.paramNames = {
13316 "multisort" : "_multisort"
13319 if(config && config.data){
13320 this.inlineData = config.data;
13321 delete config.data;
13324 Roo.apply(this, config);
13326 if(this.reader){ // reader passed
13327 this.reader = Roo.factory(this.reader, Roo.data);
13328 this.reader.xmodule = this.xmodule || false;
13329 if(!this.recordType){
13330 this.recordType = this.reader.recordType;
13332 if(this.reader.onMetaChange){
13333 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13337 if(this.recordType){
13338 this.fields = this.recordType.prototype.fields;
13340 this.modified = [];
13344 * @event datachanged
13345 * Fires when the data cache has changed, and a widget which is using this Store
13346 * as a Record cache should refresh its view.
13347 * @param {Store} this
13349 datachanged : true,
13351 * @event metachange
13352 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13353 * @param {Store} this
13354 * @param {Object} meta The JSON metadata
13359 * Fires when Records have been added to the Store
13360 * @param {Store} this
13361 * @param {Roo.data.Record[]} records The array of Records added
13362 * @param {Number} index The index at which the record(s) were added
13367 * Fires when a Record has been removed from the Store
13368 * @param {Store} this
13369 * @param {Roo.data.Record} record The Record that was removed
13370 * @param {Number} index The index at which the record was removed
13375 * Fires when a Record has been updated
13376 * @param {Store} this
13377 * @param {Roo.data.Record} record The Record that was updated
13378 * @param {String} operation The update operation being performed. Value may be one of:
13380 Roo.data.Record.EDIT
13381 Roo.data.Record.REJECT
13382 Roo.data.Record.COMMIT
13388 * Fires when the data cache has been cleared.
13389 * @param {Store} this
13393 * @event beforeload
13394 * Fires before a request is made for a new data object. If the beforeload handler returns false
13395 * the load action will be canceled.
13396 * @param {Store} this
13397 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13401 * @event beforeloadadd
13402 * Fires after a new set of Records has been loaded.
13403 * @param {Store} this
13404 * @param {Roo.data.Record[]} records The Records that were loaded
13405 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13407 beforeloadadd : true,
13410 * Fires after a new set of Records has been loaded, before they are added to the store.
13411 * @param {Store} this
13412 * @param {Roo.data.Record[]} records The Records that were loaded
13413 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13414 * @params {Object} return from reader
13418 * @event loadexception
13419 * Fires if an exception occurs in the Proxy during loading.
13420 * Called with the signature of the Proxy's "loadexception" event.
13421 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13424 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13425 * @param {Object} load options
13426 * @param {Object} jsonData from your request (normally this contains the Exception)
13428 loadexception : true
13432 this.proxy = Roo.factory(this.proxy, Roo.data);
13433 this.proxy.xmodule = this.xmodule || false;
13434 this.relayEvents(this.proxy, ["loadexception"]);
13436 this.sortToggle = {};
13437 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13439 Roo.data.Store.superclass.constructor.call(this);
13441 if(this.inlineData){
13442 this.loadData(this.inlineData);
13443 delete this.inlineData;
13447 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13449 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13450 * without a remote query - used by combo/forms at present.
13454 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13457 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13460 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13461 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13464 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13465 * on any HTTP request
13468 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13471 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13475 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13476 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13478 remoteSort : false,
13481 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13482 * loaded or when a record is removed. (defaults to false).
13484 pruneModifiedRecords : false,
13487 lastOptions : null,
13490 * Add Records to the Store and fires the add event.
13491 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13493 add : function(records){
13494 records = [].concat(records);
13495 for(var i = 0, len = records.length; i < len; i++){
13496 records[i].join(this);
13498 var index = this.data.length;
13499 this.data.addAll(records);
13500 this.fireEvent("add", this, records, index);
13504 * Remove a Record from the Store and fires the remove event.
13505 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13507 remove : function(record){
13508 var index = this.data.indexOf(record);
13509 this.data.removeAt(index);
13511 if(this.pruneModifiedRecords){
13512 this.modified.remove(record);
13514 this.fireEvent("remove", this, record, index);
13518 * Remove all Records from the Store and fires the clear event.
13520 removeAll : function(){
13522 if(this.pruneModifiedRecords){
13523 this.modified = [];
13525 this.fireEvent("clear", this);
13529 * Inserts Records to the Store at the given index and fires the add event.
13530 * @param {Number} index The start index at which to insert the passed Records.
13531 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13533 insert : function(index, records){
13534 records = [].concat(records);
13535 for(var i = 0, len = records.length; i < len; i++){
13536 this.data.insert(index, records[i]);
13537 records[i].join(this);
13539 this.fireEvent("add", this, records, index);
13543 * Get the index within the cache of the passed Record.
13544 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13545 * @return {Number} The index of the passed Record. Returns -1 if not found.
13547 indexOf : function(record){
13548 return this.data.indexOf(record);
13552 * Get the index within the cache of the Record with the passed id.
13553 * @param {String} id The id of the Record to find.
13554 * @return {Number} The index of the Record. Returns -1 if not found.
13556 indexOfId : function(id){
13557 return this.data.indexOfKey(id);
13561 * Get the Record with the specified id.
13562 * @param {String} id The id of the Record to find.
13563 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13565 getById : function(id){
13566 return this.data.key(id);
13570 * Get the Record at the specified index.
13571 * @param {Number} index The index of the Record to find.
13572 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13574 getAt : function(index){
13575 return this.data.itemAt(index);
13579 * Returns a range of Records between specified indices.
13580 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13581 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13582 * @return {Roo.data.Record[]} An array of Records
13584 getRange : function(start, end){
13585 return this.data.getRange(start, end);
13589 storeOptions : function(o){
13590 o = Roo.apply({}, o);
13593 this.lastOptions = o;
13597 * Loads the Record cache from the configured Proxy using the configured Reader.
13599 * If using remote paging, then the first load call must specify the <em>start</em>
13600 * and <em>limit</em> properties in the options.params property to establish the initial
13601 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13603 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13604 * and this call will return before the new data has been loaded. Perform any post-processing
13605 * in a callback function, or in a "load" event handler.</strong>
13607 * @param {Object} options An object containing properties which control loading options:<ul>
13608 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13609 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13610 * passed the following arguments:<ul>
13611 * <li>r : Roo.data.Record[]</li>
13612 * <li>options: Options object from the load call</li>
13613 * <li>success: Boolean success indicator</li></ul></li>
13614 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13615 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13618 load : function(options){
13619 options = options || {};
13620 if(this.fireEvent("beforeload", this, options) !== false){
13621 this.storeOptions(options);
13622 var p = Roo.apply(options.params || {}, this.baseParams);
13623 // if meta was not loaded from remote source.. try requesting it.
13624 if (!this.reader.metaFromRemote) {
13625 p._requestMeta = 1;
13627 if(this.sortInfo && this.remoteSort){
13628 var pn = this.paramNames;
13629 p[pn["sort"]] = this.sortInfo.field;
13630 p[pn["dir"]] = this.sortInfo.direction;
13632 if (this.multiSort) {
13633 var pn = this.paramNames;
13634 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13637 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13642 * Reloads the Record cache from the configured Proxy using the configured Reader and
13643 * the options from the last load operation performed.
13644 * @param {Object} options (optional) An object containing properties which may override the options
13645 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13646 * the most recently used options are reused).
13648 reload : function(options){
13649 this.load(Roo.applyIf(options||{}, this.lastOptions));
13653 // Called as a callback by the Reader during a load operation.
13654 loadRecords : function(o, options, success){
13655 if(!o || success === false){
13656 if(success !== false){
13657 this.fireEvent("load", this, [], options, o);
13659 if(options.callback){
13660 options.callback.call(options.scope || this, [], options, false);
13664 // if data returned failure - throw an exception.
13665 if (o.success === false) {
13666 // show a message if no listener is registered.
13667 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13668 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13670 // loadmask wil be hooked into this..
13671 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13674 var r = o.records, t = o.totalRecords || r.length;
13676 this.fireEvent("beforeloadadd", this, r, options, o);
13678 if(!options || options.add !== true){
13679 if(this.pruneModifiedRecords){
13680 this.modified = [];
13682 for(var i = 0, len = r.length; i < len; i++){
13686 this.data = this.snapshot;
13687 delete this.snapshot;
13690 this.data.addAll(r);
13691 this.totalLength = t;
13693 this.fireEvent("datachanged", this);
13695 this.totalLength = Math.max(t, this.data.length+r.length);
13699 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13701 var e = new Roo.data.Record({});
13703 e.set(this.parent.displayField, this.parent.emptyTitle);
13704 e.set(this.parent.valueField, '');
13709 this.fireEvent("load", this, r, options, o);
13710 if(options.callback){
13711 options.callback.call(options.scope || this, r, options, true);
13717 * Loads data from a passed data block. A Reader which understands the format of the data
13718 * must have been configured in the constructor.
13719 * @param {Object} data The data block from which to read the Records. The format of the data expected
13720 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13721 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13723 loadData : function(o, append){
13724 var r = this.reader.readRecords(o);
13725 this.loadRecords(r, {add: append}, true);
13729 * using 'cn' the nested child reader read the child array into it's child stores.
13730 * @param {Object} rec The record with a 'children array
13732 loadDataFromChildren : function(rec)
13734 this.loadData(this.reader.toLoadData(rec));
13739 * Gets the number of cached records.
13741 * <em>If using paging, this may not be the total size of the dataset. If the data object
13742 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13743 * the data set size</em>
13745 getCount : function(){
13746 return this.data.length || 0;
13750 * Gets the total number of records in the dataset as returned by the server.
13752 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13753 * the dataset size</em>
13755 getTotalCount : function(){
13756 return this.totalLength || 0;
13760 * Returns the sort state of the Store as an object with two properties:
13762 field {String} The name of the field by which the Records are sorted
13763 direction {String} The sort order, "ASC" or "DESC"
13766 getSortState : function(){
13767 return this.sortInfo;
13771 applySort : function(){
13772 if(this.sortInfo && !this.remoteSort){
13773 var s = this.sortInfo, f = s.field;
13774 var st = this.fields.get(f).sortType;
13775 var fn = function(r1, r2){
13776 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13777 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13779 this.data.sort(s.direction, fn);
13780 if(this.snapshot && this.snapshot != this.data){
13781 this.snapshot.sort(s.direction, fn);
13787 * Sets the default sort column and order to be used by the next load operation.
13788 * @param {String} fieldName The name of the field to sort by.
13789 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13791 setDefaultSort : function(field, dir){
13792 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13796 * Sort the Records.
13797 * If remote sorting is used, the sort is performed on the server, and the cache is
13798 * reloaded. If local sorting is used, the cache is sorted internally.
13799 * @param {String} fieldName The name of the field to sort by.
13800 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13802 sort : function(fieldName, dir){
13803 var f = this.fields.get(fieldName);
13805 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13807 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13808 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13813 this.sortToggle[f.name] = dir;
13814 this.sortInfo = {field: f.name, direction: dir};
13815 if(!this.remoteSort){
13817 this.fireEvent("datachanged", this);
13819 this.load(this.lastOptions);
13824 * Calls the specified function for each of the Records in the cache.
13825 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13826 * Returning <em>false</em> aborts and exits the iteration.
13827 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13829 each : function(fn, scope){
13830 this.data.each(fn, scope);
13834 * Gets all records modified since the last commit. Modified records are persisted across load operations
13835 * (e.g., during paging).
13836 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13838 getModifiedRecords : function(){
13839 return this.modified;
13843 createFilterFn : function(property, value, anyMatch){
13844 if(!value.exec){ // not a regex
13845 value = String(value);
13846 if(value.length == 0){
13849 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13851 return function(r){
13852 return value.test(r.data[property]);
13857 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13858 * @param {String} property A field on your records
13859 * @param {Number} start The record index to start at (defaults to 0)
13860 * @param {Number} end The last record index to include (defaults to length - 1)
13861 * @return {Number} The sum
13863 sum : function(property, start, end){
13864 var rs = this.data.items, v = 0;
13865 start = start || 0;
13866 end = (end || end === 0) ? end : rs.length-1;
13868 for(var i = start; i <= end; i++){
13869 v += (rs[i].data[property] || 0);
13875 * Filter the records by a specified property.
13876 * @param {String} field A field on your records
13877 * @param {String/RegExp} value Either a string that the field
13878 * should start with or a RegExp to test against the field
13879 * @param {Boolean} anyMatch True to match any part not just the beginning
13881 filter : function(property, value, anyMatch){
13882 var fn = this.createFilterFn(property, value, anyMatch);
13883 return fn ? this.filterBy(fn) : this.clearFilter();
13887 * Filter by a function. The specified function will be called with each
13888 * record in this data source. If the function returns true the record is included,
13889 * otherwise it is filtered.
13890 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13891 * @param {Object} scope (optional) The scope of the function (defaults to this)
13893 filterBy : function(fn, scope){
13894 this.snapshot = this.snapshot || this.data;
13895 this.data = this.queryBy(fn, scope||this);
13896 this.fireEvent("datachanged", this);
13900 * Query the records by a specified property.
13901 * @param {String} field A field on your records
13902 * @param {String/RegExp} value Either a string that the field
13903 * should start with or a RegExp to test against the field
13904 * @param {Boolean} anyMatch True to match any part not just the beginning
13905 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13907 query : function(property, value, anyMatch){
13908 var fn = this.createFilterFn(property, value, anyMatch);
13909 return fn ? this.queryBy(fn) : this.data.clone();
13913 * Query by a function. The specified function will be called with each
13914 * record in this data source. If the function returns true the record is included
13916 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13917 * @param {Object} scope (optional) The scope of the function (defaults to this)
13918 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13920 queryBy : function(fn, scope){
13921 var data = this.snapshot || this.data;
13922 return data.filterBy(fn, scope||this);
13926 * Collects unique values for a particular dataIndex from this store.
13927 * @param {String} dataIndex The property to collect
13928 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13929 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13930 * @return {Array} An array of the unique values
13932 collect : function(dataIndex, allowNull, bypassFilter){
13933 var d = (bypassFilter === true && this.snapshot) ?
13934 this.snapshot.items : this.data.items;
13935 var v, sv, r = [], l = {};
13936 for(var i = 0, len = d.length; i < len; i++){
13937 v = d[i].data[dataIndex];
13939 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13948 * Revert to a view of the Record cache with no filtering applied.
13949 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13951 clearFilter : function(suppressEvent){
13952 if(this.snapshot && this.snapshot != this.data){
13953 this.data = this.snapshot;
13954 delete this.snapshot;
13955 if(suppressEvent !== true){
13956 this.fireEvent("datachanged", this);
13962 afterEdit : function(record){
13963 if(this.modified.indexOf(record) == -1){
13964 this.modified.push(record);
13966 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13970 afterReject : function(record){
13971 this.modified.remove(record);
13972 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13976 afterCommit : function(record){
13977 this.modified.remove(record);
13978 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13982 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13983 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13985 commitChanges : function(){
13986 var m = this.modified.slice(0);
13987 this.modified = [];
13988 for(var i = 0, len = m.length; i < len; i++){
13994 * Cancel outstanding changes on all changed records.
13996 rejectChanges : function(){
13997 var m = this.modified.slice(0);
13998 this.modified = [];
13999 for(var i = 0, len = m.length; i < len; i++){
14004 onMetaChange : function(meta, rtype, o){
14005 this.recordType = rtype;
14006 this.fields = rtype.prototype.fields;
14007 delete this.snapshot;
14008 this.sortInfo = meta.sortInfo || this.sortInfo;
14009 this.modified = [];
14010 this.fireEvent('metachange', this, this.reader.meta);
14013 moveIndex : function(data, type)
14015 var index = this.indexOf(data);
14017 var newIndex = index + type;
14021 this.insert(newIndex, data);
14026 * Ext JS Library 1.1.1
14027 * Copyright(c) 2006-2007, Ext JS, LLC.
14029 * Originally Released Under LGPL - original licence link has changed is not relivant.
14032 * <script type="text/javascript">
14036 * @class Roo.data.SimpleStore
14037 * @extends Roo.data.Store
14038 * Small helper class to make creating Stores from Array data easier.
14039 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14040 * @cfg {Array} fields An array of field definition objects, or field name strings.
14041 * @cfg {Object} an existing reader (eg. copied from another store)
14042 * @cfg {Array} data The multi-dimensional array of data
14044 * @param {Object} config
14046 Roo.data.SimpleStore = function(config)
14048 Roo.data.SimpleStore.superclass.constructor.call(this, {
14050 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14053 Roo.data.Record.create(config.fields)
14055 proxy : new Roo.data.MemoryProxy(config.data)
14059 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14061 * Ext JS Library 1.1.1
14062 * Copyright(c) 2006-2007, Ext JS, LLC.
14064 * Originally Released Under LGPL - original licence link has changed is not relivant.
14067 * <script type="text/javascript">
14072 * @extends Roo.data.Store
14073 * @class Roo.data.JsonStore
14074 * Small helper class to make creating Stores for JSON data easier. <br/>
14076 var store = new Roo.data.JsonStore({
14077 url: 'get-images.php',
14079 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14082 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14083 * JsonReader and HttpProxy (unless inline data is provided).</b>
14084 * @cfg {Array} fields An array of field definition objects, or field name strings.
14086 * @param {Object} config
14088 Roo.data.JsonStore = function(c){
14089 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14090 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14091 reader: new Roo.data.JsonReader(c, c.fields)
14094 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14096 * Ext JS Library 1.1.1
14097 * Copyright(c) 2006-2007, Ext JS, LLC.
14099 * Originally Released Under LGPL - original licence link has changed is not relivant.
14102 * <script type="text/javascript">
14106 Roo.data.Field = function(config){
14107 if(typeof config == "string"){
14108 config = {name: config};
14110 Roo.apply(this, config);
14113 this.type = "auto";
14116 var st = Roo.data.SortTypes;
14117 // named sortTypes are supported, here we look them up
14118 if(typeof this.sortType == "string"){
14119 this.sortType = st[this.sortType];
14122 // set default sortType for strings and dates
14123 if(!this.sortType){
14126 this.sortType = st.asUCString;
14129 this.sortType = st.asDate;
14132 this.sortType = st.none;
14137 var stripRe = /[\$,%]/g;
14139 // prebuilt conversion function for this field, instead of
14140 // switching every time we're reading a value
14142 var cv, dateFormat = this.dateFormat;
14147 cv = function(v){ return v; };
14150 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14154 return v !== undefined && v !== null && v !== '' ?
14155 parseInt(String(v).replace(stripRe, ""), 10) : '';
14160 return v !== undefined && v !== null && v !== '' ?
14161 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14166 cv = function(v){ return v === true || v === "true" || v == 1; };
14173 if(v instanceof Date){
14177 if(dateFormat == "timestamp"){
14178 return new Date(v*1000);
14180 return Date.parseDate(v, dateFormat);
14182 var parsed = Date.parse(v);
14183 return parsed ? new Date(parsed) : null;
14192 Roo.data.Field.prototype = {
14200 * Ext JS Library 1.1.1
14201 * Copyright(c) 2006-2007, Ext JS, LLC.
14203 * Originally Released Under LGPL - original licence link has changed is not relivant.
14206 * <script type="text/javascript">
14209 // Base class for reading structured data from a data source. This class is intended to be
14210 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14213 * @class Roo.data.DataReader
14214 * Base class for reading structured data from a data source. This class is intended to be
14215 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14218 Roo.data.DataReader = function(meta, recordType){
14222 this.recordType = recordType instanceof Array ?
14223 Roo.data.Record.create(recordType) : recordType;
14226 Roo.data.DataReader.prototype = {
14229 readerType : 'Data',
14231 * Create an empty record
14232 * @param {Object} data (optional) - overlay some values
14233 * @return {Roo.data.Record} record created.
14235 newRow : function(d) {
14237 this.recordType.prototype.fields.each(function(c) {
14239 case 'int' : da[c.name] = 0; break;
14240 case 'date' : da[c.name] = new Date(); break;
14241 case 'float' : da[c.name] = 0.0; break;
14242 case 'boolean' : da[c.name] = false; break;
14243 default : da[c.name] = ""; break;
14247 return new this.recordType(Roo.apply(da, d));
14253 * Ext JS Library 1.1.1
14254 * Copyright(c) 2006-2007, Ext JS, LLC.
14256 * Originally Released Under LGPL - original licence link has changed is not relivant.
14259 * <script type="text/javascript">
14263 * @class Roo.data.DataProxy
14264 * @extends Roo.data.Observable
14265 * This class is an abstract base class for implementations which provide retrieval of
14266 * unformatted data objects.<br>
14268 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14269 * (of the appropriate type which knows how to parse the data object) to provide a block of
14270 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14272 * Custom implementations must implement the load method as described in
14273 * {@link Roo.data.HttpProxy#load}.
14275 Roo.data.DataProxy = function(){
14278 * @event beforeload
14279 * Fires before a network request is made to retrieve a data object.
14280 * @param {Object} This DataProxy object.
14281 * @param {Object} params The params parameter to the load function.
14286 * Fires before the load method's callback is called.
14287 * @param {Object} This DataProxy object.
14288 * @param {Object} o The data object.
14289 * @param {Object} arg The callback argument object passed to the load function.
14293 * @event loadexception
14294 * Fires if an Exception occurs during data retrieval.
14295 * @param {Object} This DataProxy object.
14296 * @param {Object} o The data object.
14297 * @param {Object} arg The callback argument object passed to the load function.
14298 * @param {Object} e The Exception.
14300 loadexception : true
14302 Roo.data.DataProxy.superclass.constructor.call(this);
14305 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14308 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14312 * Ext JS Library 1.1.1
14313 * Copyright(c) 2006-2007, Ext JS, LLC.
14315 * Originally Released Under LGPL - original licence link has changed is not relivant.
14318 * <script type="text/javascript">
14321 * @class Roo.data.MemoryProxy
14322 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14323 * to the Reader when its load method is called.
14325 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14327 Roo.data.MemoryProxy = function(data){
14331 Roo.data.MemoryProxy.superclass.constructor.call(this);
14335 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14338 * Load data from the requested source (in this case an in-memory
14339 * data object passed to the constructor), read the data object into
14340 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14341 * process that block using the passed callback.
14342 * @param {Object} params This parameter is not used by the MemoryProxy class.
14343 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14344 * object into a block of Roo.data.Records.
14345 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14346 * The function must be passed <ul>
14347 * <li>The Record block object</li>
14348 * <li>The "arg" argument from the load function</li>
14349 * <li>A boolean success indicator</li>
14351 * @param {Object} scope The scope in which to call the callback
14352 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14354 load : function(params, reader, callback, scope, arg){
14355 params = params || {};
14358 result = reader.readRecords(params.data ? params.data :this.data);
14360 this.fireEvent("loadexception", this, arg, null, e);
14361 callback.call(scope, null, arg, false);
14364 callback.call(scope, result, arg, true);
14368 update : function(params, records){
14373 * Ext JS Library 1.1.1
14374 * Copyright(c) 2006-2007, Ext JS, LLC.
14376 * Originally Released Under LGPL - original licence link has changed is not relivant.
14379 * <script type="text/javascript">
14382 * @class Roo.data.HttpProxy
14383 * @extends Roo.data.DataProxy
14384 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14385 * configured to reference a certain URL.<br><br>
14387 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14388 * from which the running page was served.<br><br>
14390 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14392 * Be aware that to enable the browser to parse an XML document, the server must set
14393 * the Content-Type header in the HTTP response to "text/xml".
14395 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14396 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14397 * will be used to make the request.
14399 Roo.data.HttpProxy = function(conn){
14400 Roo.data.HttpProxy.superclass.constructor.call(this);
14401 // is conn a conn config or a real conn?
14403 this.useAjax = !conn || !conn.events;
14407 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14408 // thse are take from connection...
14411 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14414 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14415 * extra parameters to each request made by this object. (defaults to undefined)
14418 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14419 * to each request made by this object. (defaults to undefined)
14422 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14425 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14428 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14434 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14438 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14439 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14440 * a finer-grained basis than the DataProxy events.
14442 getConnection : function(){
14443 return this.useAjax ? Roo.Ajax : this.conn;
14447 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14448 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14449 * process that block using the passed callback.
14450 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14451 * for the request to the remote server.
14452 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14453 * object into a block of Roo.data.Records.
14454 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14455 * The function must be passed <ul>
14456 * <li>The Record block object</li>
14457 * <li>The "arg" argument from the load function</li>
14458 * <li>A boolean success indicator</li>
14460 * @param {Object} scope The scope in which to call the callback
14461 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14463 load : function(params, reader, callback, scope, arg){
14464 if(this.fireEvent("beforeload", this, params) !== false){
14466 params : params || {},
14468 callback : callback,
14473 callback : this.loadResponse,
14477 Roo.applyIf(o, this.conn);
14478 if(this.activeRequest){
14479 Roo.Ajax.abort(this.activeRequest);
14481 this.activeRequest = Roo.Ajax.request(o);
14483 this.conn.request(o);
14486 callback.call(scope||this, null, arg, false);
14491 loadResponse : function(o, success, response){
14492 delete this.activeRequest;
14494 this.fireEvent("loadexception", this, o, response);
14495 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14500 result = o.reader.read(response);
14502 this.fireEvent("loadexception", this, o, response, e);
14503 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14507 this.fireEvent("load", this, o, o.request.arg);
14508 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14512 update : function(dataSet){
14517 updateResponse : function(dataSet){
14522 * Ext JS Library 1.1.1
14523 * Copyright(c) 2006-2007, Ext JS, LLC.
14525 * Originally Released Under LGPL - original licence link has changed is not relivant.
14528 * <script type="text/javascript">
14532 * @class Roo.data.ScriptTagProxy
14533 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14534 * other than the originating domain of the running page.<br><br>
14536 * <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
14537 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14539 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14540 * source code that is used as the source inside a <script> tag.<br><br>
14542 * In order for the browser to process the returned data, the server must wrap the data object
14543 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14544 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14545 * depending on whether the callback name was passed:
14548 boolean scriptTag = false;
14549 String cb = request.getParameter("callback");
14552 response.setContentType("text/javascript");
14554 response.setContentType("application/x-json");
14556 Writer out = response.getWriter();
14558 out.write(cb + "(");
14560 out.print(dataBlock.toJsonString());
14567 * @param {Object} config A configuration object.
14569 Roo.data.ScriptTagProxy = function(config){
14570 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14571 Roo.apply(this, config);
14572 this.head = document.getElementsByTagName("head")[0];
14575 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14577 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14579 * @cfg {String} url The URL from which to request the data object.
14582 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14586 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14587 * the server the name of the callback function set up by the load call to process the returned data object.
14588 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14589 * javascript output which calls this named function passing the data object as its only parameter.
14591 callbackParam : "callback",
14593 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14594 * name to the request.
14599 * Load data from the configured URL, read the data object into
14600 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14601 * process that block using the passed callback.
14602 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14603 * for the request to the remote server.
14604 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14605 * object into a block of Roo.data.Records.
14606 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14607 * The function must be passed <ul>
14608 * <li>The Record block object</li>
14609 * <li>The "arg" argument from the load function</li>
14610 * <li>A boolean success indicator</li>
14612 * @param {Object} scope The scope in which to call the callback
14613 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14615 load : function(params, reader, callback, scope, arg){
14616 if(this.fireEvent("beforeload", this, params) !== false){
14618 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14620 var url = this.url;
14621 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14623 url += "&_dc=" + (new Date().getTime());
14625 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14628 cb : "stcCallback"+transId,
14629 scriptId : "stcScript"+transId,
14633 callback : callback,
14639 window[trans.cb] = function(o){
14640 conn.handleResponse(o, trans);
14643 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14645 if(this.autoAbort !== false){
14649 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14651 var script = document.createElement("script");
14652 script.setAttribute("src", url);
14653 script.setAttribute("type", "text/javascript");
14654 script.setAttribute("id", trans.scriptId);
14655 this.head.appendChild(script);
14657 this.trans = trans;
14659 callback.call(scope||this, null, arg, false);
14664 isLoading : function(){
14665 return this.trans ? true : false;
14669 * Abort the current server request.
14671 abort : function(){
14672 if(this.isLoading()){
14673 this.destroyTrans(this.trans);
14678 destroyTrans : function(trans, isLoaded){
14679 this.head.removeChild(document.getElementById(trans.scriptId));
14680 clearTimeout(trans.timeoutId);
14682 window[trans.cb] = undefined;
14684 delete window[trans.cb];
14687 // if hasn't been loaded, wait for load to remove it to prevent script error
14688 window[trans.cb] = function(){
14689 window[trans.cb] = undefined;
14691 delete window[trans.cb];
14698 handleResponse : function(o, trans){
14699 this.trans = false;
14700 this.destroyTrans(trans, true);
14703 result = trans.reader.readRecords(o);
14705 this.fireEvent("loadexception", this, o, trans.arg, e);
14706 trans.callback.call(trans.scope||window, null, trans.arg, false);
14709 this.fireEvent("load", this, o, trans.arg);
14710 trans.callback.call(trans.scope||window, result, trans.arg, true);
14714 handleFailure : function(trans){
14715 this.trans = false;
14716 this.destroyTrans(trans, false);
14717 this.fireEvent("loadexception", this, null, trans.arg);
14718 trans.callback.call(trans.scope||window, null, trans.arg, false);
14722 * Ext JS Library 1.1.1
14723 * Copyright(c) 2006-2007, Ext JS, LLC.
14725 * Originally Released Under LGPL - original licence link has changed is not relivant.
14728 * <script type="text/javascript">
14732 * @class Roo.data.JsonReader
14733 * @extends Roo.data.DataReader
14734 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14735 * based on mappings in a provided Roo.data.Record constructor.
14737 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14738 * in the reply previously.
14743 var RecordDef = Roo.data.Record.create([
14744 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14745 {name: 'occupation'} // This field will use "occupation" as the mapping.
14747 var myReader = new Roo.data.JsonReader({
14748 totalProperty: "results", // The property which contains the total dataset size (optional)
14749 root: "rows", // The property which contains an Array of row objects
14750 id: "id" // The property within each row object that provides an ID for the record (optional)
14754 * This would consume a JSON file like this:
14756 { 'results': 2, 'rows': [
14757 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14758 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14761 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14762 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14763 * paged from the remote server.
14764 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14765 * @cfg {String} root name of the property which contains the Array of row objects.
14766 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14767 * @cfg {Array} fields Array of field definition objects
14769 * Create a new JsonReader
14770 * @param {Object} meta Metadata configuration options
14771 * @param {Object} recordType Either an Array of field definition objects,
14772 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14774 Roo.data.JsonReader = function(meta, recordType){
14777 // set some defaults:
14778 Roo.applyIf(meta, {
14779 totalProperty: 'total',
14780 successProperty : 'success',
14785 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14787 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14789 readerType : 'Json',
14792 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14793 * Used by Store query builder to append _requestMeta to params.
14796 metaFromRemote : false,
14798 * This method is only used by a DataProxy which has retrieved data from a remote server.
14799 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14800 * @return {Object} data A data block which is used by an Roo.data.Store object as
14801 * a cache of Roo.data.Records.
14803 read : function(response){
14804 var json = response.responseText;
14806 var o = /* eval:var:o */ eval("("+json+")");
14808 throw {message: "JsonReader.read: Json object not found"};
14814 this.metaFromRemote = true;
14815 this.meta = o.metaData;
14816 this.recordType = Roo.data.Record.create(o.metaData.fields);
14817 this.onMetaChange(this.meta, this.recordType, o);
14819 return this.readRecords(o);
14822 // private function a store will implement
14823 onMetaChange : function(meta, recordType, o){
14830 simpleAccess: function(obj, subsc) {
14837 getJsonAccessor: function(){
14839 return function(expr) {
14841 return(re.test(expr))
14842 ? new Function("obj", "return obj." + expr)
14847 return Roo.emptyFn;
14852 * Create a data block containing Roo.data.Records from an XML document.
14853 * @param {Object} o An object which contains an Array of row objects in the property specified
14854 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14855 * which contains the total size of the dataset.
14856 * @return {Object} data A data block which is used by an Roo.data.Store object as
14857 * a cache of Roo.data.Records.
14859 readRecords : function(o){
14861 * After any data loads, the raw JSON data is available for further custom processing.
14865 var s = this.meta, Record = this.recordType,
14866 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14868 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14870 if(s.totalProperty) {
14871 this.getTotal = this.getJsonAccessor(s.totalProperty);
14873 if(s.successProperty) {
14874 this.getSuccess = this.getJsonAccessor(s.successProperty);
14876 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14878 var g = this.getJsonAccessor(s.id);
14879 this.getId = function(rec) {
14881 return (r === undefined || r === "") ? null : r;
14884 this.getId = function(){return null;};
14887 for(var jj = 0; jj < fl; jj++){
14889 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14890 this.ef[jj] = this.getJsonAccessor(map);
14894 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14895 if(s.totalProperty){
14896 var vt = parseInt(this.getTotal(o), 10);
14901 if(s.successProperty){
14902 var vs = this.getSuccess(o);
14903 if(vs === false || vs === 'false'){
14908 for(var i = 0; i < c; i++){
14911 var id = this.getId(n);
14912 for(var j = 0; j < fl; j++){
14914 var v = this.ef[j](n);
14916 Roo.log('missing convert for ' + f.name);
14920 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14922 var record = new Record(values, id);
14924 records[i] = record;
14930 totalRecords : totalRecords
14933 // used when loading children.. @see loadDataFromChildren
14934 toLoadData: function(rec)
14936 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14937 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14938 return { data : data, total : data.length };
14943 * Ext JS Library 1.1.1
14944 * Copyright(c) 2006-2007, Ext JS, LLC.
14946 * Originally Released Under LGPL - original licence link has changed is not relivant.
14949 * <script type="text/javascript">
14953 * @class Roo.data.ArrayReader
14954 * @extends Roo.data.DataReader
14955 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14956 * Each element of that Array represents a row of data fields. The
14957 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14958 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14962 var RecordDef = Roo.data.Record.create([
14963 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14964 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14966 var myReader = new Roo.data.ArrayReader({
14967 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14971 * This would consume an Array like this:
14973 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14977 * Create a new JsonReader
14978 * @param {Object} meta Metadata configuration options.
14979 * @param {Object|Array} recordType Either an Array of field definition objects
14981 * @cfg {Array} fields Array of field definition objects
14982 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14983 * as specified to {@link Roo.data.Record#create},
14984 * or an {@link Roo.data.Record} object
14987 * created using {@link Roo.data.Record#create}.
14989 Roo.data.ArrayReader = function(meta, recordType)
14991 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14994 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14997 * Create a data block containing Roo.data.Records from an XML document.
14998 * @param {Object} o An Array of row objects which represents the dataset.
14999 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15000 * a cache of Roo.data.Records.
15002 readRecords : function(o)
15004 var sid = this.meta ? this.meta.id : null;
15005 var recordType = this.recordType, fields = recordType.prototype.fields;
15008 for(var i = 0; i < root.length; i++){
15011 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15012 for(var j = 0, jlen = fields.length; j < jlen; j++){
15013 var f = fields.items[j];
15014 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15015 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15017 values[f.name] = v;
15019 var record = new recordType(values, id);
15021 records[records.length] = record;
15025 totalRecords : records.length
15028 // used when loading children.. @see loadDataFromChildren
15029 toLoadData: function(rec)
15031 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15032 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15043 * @class Roo.bootstrap.ComboBox
15044 * @extends Roo.bootstrap.TriggerField
15045 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15046 * @cfg {Boolean} append (true|false) default false
15047 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15048 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15049 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15050 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15051 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15052 * @cfg {Boolean} animate default true
15053 * @cfg {Boolean} emptyResultText only for touch device
15054 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15055 * @cfg {String} emptyTitle default ''
15056 * @cfg {Number} width fixed with? experimental
15058 * Create a new ComboBox.
15059 * @param {Object} config Configuration options
15061 Roo.bootstrap.ComboBox = function(config){
15062 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15066 * Fires when the dropdown list is expanded
15067 * @param {Roo.bootstrap.ComboBox} combo This combo box
15072 * Fires when the dropdown list is collapsed
15073 * @param {Roo.bootstrap.ComboBox} combo This combo box
15077 * @event beforeselect
15078 * Fires before a list item is selected. Return false to cancel the selection.
15079 * @param {Roo.bootstrap.ComboBox} combo This combo box
15080 * @param {Roo.data.Record} record The data record returned from the underlying store
15081 * @param {Number} index The index of the selected item in the dropdown list
15083 'beforeselect' : true,
15086 * Fires when a list item is selected
15087 * @param {Roo.bootstrap.ComboBox} combo This combo box
15088 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15089 * @param {Number} index The index of the selected item in the dropdown list
15093 * @event beforequery
15094 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15095 * The event object passed has these properties:
15096 * @param {Roo.bootstrap.ComboBox} combo This combo box
15097 * @param {String} query The query
15098 * @param {Boolean} forceAll true to force "all" query
15099 * @param {Boolean} cancel true to cancel the query
15100 * @param {Object} e The query event object
15102 'beforequery': true,
15105 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15106 * @param {Roo.bootstrap.ComboBox} combo This combo box
15111 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15112 * @param {Roo.bootstrap.ComboBox} combo This combo box
15113 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15118 * Fires when the remove value from the combobox array
15119 * @param {Roo.bootstrap.ComboBox} combo This combo box
15123 * @event afterremove
15124 * Fires when the remove value from the combobox array
15125 * @param {Roo.bootstrap.ComboBox} combo This combo box
15127 'afterremove' : true,
15129 * @event specialfilter
15130 * Fires when specialfilter
15131 * @param {Roo.bootstrap.ComboBox} combo This combo box
15133 'specialfilter' : true,
15136 * Fires when tick the element
15137 * @param {Roo.bootstrap.ComboBox} combo This combo box
15141 * @event touchviewdisplay
15142 * Fires when touch view require special display (default is using displayField)
15143 * @param {Roo.bootstrap.ComboBox} combo This combo box
15144 * @param {Object} cfg set html .
15146 'touchviewdisplay' : true
15151 this.tickItems = [];
15153 this.selectedIndex = -1;
15154 if(this.mode == 'local'){
15155 if(config.queryDelay === undefined){
15156 this.queryDelay = 10;
15158 if(config.minChars === undefined){
15164 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15167 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15168 * rendering into an Roo.Editor, defaults to false)
15171 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15172 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15175 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15178 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15179 * the dropdown list (defaults to undefined, with no header element)
15183 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15187 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15189 listWidth: undefined,
15191 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15192 * mode = 'remote' or 'text' if mode = 'local')
15194 displayField: undefined,
15197 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15198 * mode = 'remote' or 'value' if mode = 'local').
15199 * Note: use of a valueField requires the user make a selection
15200 * in order for a value to be mapped.
15202 valueField: undefined,
15204 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15209 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15210 * field's data value (defaults to the underlying DOM element's name)
15212 hiddenName: undefined,
15214 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15218 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15220 selectedClass: 'active',
15223 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15227 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15228 * anchor positions (defaults to 'tl-bl')
15230 listAlign: 'tl-bl?',
15232 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15236 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15237 * query specified by the allQuery config option (defaults to 'query')
15239 triggerAction: 'query',
15241 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15242 * (defaults to 4, does not apply if editable = false)
15246 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15247 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15251 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15252 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15256 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15257 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15261 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15262 * when editable = true (defaults to false)
15264 selectOnFocus:false,
15266 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15268 queryParam: 'query',
15270 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15271 * when mode = 'remote' (defaults to 'Loading...')
15273 loadingText: 'Loading...',
15275 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15279 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15283 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15284 * traditional select (defaults to true)
15288 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15292 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15296 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15297 * listWidth has a higher value)
15301 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15302 * allow the user to set arbitrary text into the field (defaults to false)
15304 forceSelection:false,
15306 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15307 * if typeAhead = true (defaults to 250)
15309 typeAheadDelay : 250,
15311 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15312 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15314 valueNotFoundText : undefined,
15316 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15318 blockFocus : false,
15321 * @cfg {Boolean} disableClear Disable showing of clear button.
15323 disableClear : false,
15325 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15327 alwaysQuery : false,
15330 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15335 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15337 invalidClass : "has-warning",
15340 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15342 validClass : "has-success",
15345 * @cfg {Boolean} specialFilter (true|false) special filter default false
15347 specialFilter : false,
15350 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15352 mobileTouchView : true,
15355 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15357 useNativeIOS : false,
15360 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15362 mobile_restrict_height : false,
15364 ios_options : false,
15376 btnPosition : 'right',
15377 triggerList : true,
15378 showToggleBtn : true,
15380 emptyResultText: 'Empty',
15381 triggerText : 'Select',
15385 // element that contains real text value.. (when hidden is used..)
15387 getAutoCreate : function()
15392 * Render classic select for iso
15395 if(Roo.isIOS && this.useNativeIOS){
15396 cfg = this.getAutoCreateNativeIOS();
15404 if(Roo.isTouch && this.mobileTouchView){
15405 cfg = this.getAutoCreateTouchView();
15412 if(!this.tickable){
15413 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15418 * ComboBox with tickable selections
15421 var align = this.labelAlign || this.parentLabelAlign();
15424 cls : 'form-group roo-combobox-tickable' //input-group
15427 var btn_text_select = '';
15428 var btn_text_done = '';
15429 var btn_text_cancel = '';
15431 if (this.btn_text_show) {
15432 btn_text_select = 'Select';
15433 btn_text_done = 'Done';
15434 btn_text_cancel = 'Cancel';
15439 cls : 'tickable-buttons',
15444 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15445 //html : this.triggerText
15446 html: btn_text_select
15452 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15454 html: btn_text_done
15460 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15462 html: btn_text_cancel
15468 buttons.cn.unshift({
15470 cls: 'roo-select2-search-field-input'
15476 Roo.each(buttons.cn, function(c){
15478 c.cls += ' btn-' + _this.size;
15481 if (_this.disabled) {
15488 style : 'display: contents',
15493 cls: 'form-hidden-field'
15497 cls: 'roo-select2-choices',
15501 cls: 'roo-select2-search-field',
15512 cls: 'roo-select2-container input-group roo-select2-container-multi',
15518 // cls: 'typeahead typeahead-long dropdown-menu',
15519 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15524 if(this.hasFeedback && !this.allowBlank){
15528 cls: 'glyphicon form-control-feedback'
15531 combobox.cn.push(feedback);
15538 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15539 tooltip : 'This field is required'
15541 if (Roo.bootstrap.version == 4) {
15544 style : 'display:none'
15547 if (align ==='left' && this.fieldLabel.length) {
15549 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15556 cls : 'control-label col-form-label',
15557 html : this.fieldLabel
15569 var labelCfg = cfg.cn[1];
15570 var contentCfg = cfg.cn[2];
15573 if(this.indicatorpos == 'right'){
15579 cls : 'control-label col-form-label',
15583 html : this.fieldLabel
15599 labelCfg = cfg.cn[0];
15600 contentCfg = cfg.cn[1];
15604 if(this.labelWidth > 12){
15605 labelCfg.style = "width: " + this.labelWidth + 'px';
15607 if(this.width * 1 > 0){
15608 contentCfg.style = "width: " + this.width + 'px';
15610 if(this.labelWidth < 13 && this.labelmd == 0){
15611 this.labelmd = this.labelWidth;
15614 if(this.labellg > 0){
15615 labelCfg.cls += ' col-lg-' + this.labellg;
15616 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15619 if(this.labelmd > 0){
15620 labelCfg.cls += ' col-md-' + this.labelmd;
15621 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15624 if(this.labelsm > 0){
15625 labelCfg.cls += ' col-sm-' + this.labelsm;
15626 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15629 if(this.labelxs > 0){
15630 labelCfg.cls += ' col-xs-' + this.labelxs;
15631 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15635 } else if ( this.fieldLabel.length) {
15636 // Roo.log(" label");
15641 //cls : 'input-group-addon',
15642 html : this.fieldLabel
15647 if(this.indicatorpos == 'right'){
15651 //cls : 'input-group-addon',
15652 html : this.fieldLabel
15662 // Roo.log(" no label && no align");
15669 ['xs','sm','md','lg'].map(function(size){
15670 if (settings[size]) {
15671 cfg.cls += ' col-' + size + '-' + settings[size];
15679 _initEventsCalled : false,
15682 initEvents: function()
15684 if (this._initEventsCalled) { // as we call render... prevent looping...
15687 this._initEventsCalled = true;
15690 throw "can not find store for combo";
15693 this.indicator = this.indicatorEl();
15695 this.store = Roo.factory(this.store, Roo.data);
15696 this.store.parent = this;
15698 // if we are building from html. then this element is so complex, that we can not really
15699 // use the rendered HTML.
15700 // so we have to trash and replace the previous code.
15701 if (Roo.XComponent.build_from_html) {
15702 // remove this element....
15703 var e = this.el.dom, k=0;
15704 while (e ) { e = e.previousSibling; ++k;}
15709 this.rendered = false;
15711 this.render(this.parent().getChildContainer(true), k);
15714 if(Roo.isIOS && this.useNativeIOS){
15715 this.initIOSView();
15723 if(Roo.isTouch && this.mobileTouchView){
15724 this.initTouchView();
15729 this.initTickableEvents();
15733 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15735 if(this.hiddenName){
15737 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15739 this.hiddenField.dom.value =
15740 this.hiddenValue !== undefined ? this.hiddenValue :
15741 this.value !== undefined ? this.value : '';
15743 // prevent input submission
15744 this.el.dom.removeAttribute('name');
15745 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15750 // this.el.dom.setAttribute('autocomplete', 'off');
15753 var cls = 'x-combo-list';
15755 //this.list = new Roo.Layer({
15756 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15762 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15763 _this.list.setWidth(lw);
15766 this.list.on('mouseover', this.onViewOver, this);
15767 this.list.on('mousemove', this.onViewMove, this);
15768 this.list.on('scroll', this.onViewScroll, this);
15771 this.list.swallowEvent('mousewheel');
15772 this.assetHeight = 0;
15775 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15776 this.assetHeight += this.header.getHeight();
15779 this.innerList = this.list.createChild({cls:cls+'-inner'});
15780 this.innerList.on('mouseover', this.onViewOver, this);
15781 this.innerList.on('mousemove', this.onViewMove, this);
15782 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15784 if(this.allowBlank && !this.pageSize && !this.disableClear){
15785 this.footer = this.list.createChild({cls:cls+'-ft'});
15786 this.pageTb = new Roo.Toolbar(this.footer);
15790 this.footer = this.list.createChild({cls:cls+'-ft'});
15791 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15792 {pageSize: this.pageSize});
15796 if (this.pageTb && this.allowBlank && !this.disableClear) {
15798 this.pageTb.add(new Roo.Toolbar.Fill(), {
15799 cls: 'x-btn-icon x-btn-clear',
15801 handler: function()
15804 _this.clearValue();
15805 _this.onSelect(false, -1);
15810 this.assetHeight += this.footer.getHeight();
15815 this.tpl = Roo.bootstrap.version == 4 ?
15816 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15817 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15820 this.view = new Roo.View(this.list, this.tpl, {
15821 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15823 //this.view.wrapEl.setDisplayed(false);
15824 this.view.on('click', this.onViewClick, this);
15827 this.store.on('beforeload', this.onBeforeLoad, this);
15828 this.store.on('load', this.onLoad, this);
15829 this.store.on('loadexception', this.onLoadException, this);
15831 if(this.resizable){
15832 this.resizer = new Roo.Resizable(this.list, {
15833 pinned:true, handles:'se'
15835 this.resizer.on('resize', function(r, w, h){
15836 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15837 this.listWidth = w;
15838 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15839 this.restrictHeight();
15841 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15844 if(!this.editable){
15845 this.editable = true;
15846 this.setEditable(false);
15851 if (typeof(this.events.add.listeners) != 'undefined') {
15853 this.addicon = this.wrap.createChild(
15854 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15856 this.addicon.on('click', function(e) {
15857 this.fireEvent('add', this);
15860 if (typeof(this.events.edit.listeners) != 'undefined') {
15862 this.editicon = this.wrap.createChild(
15863 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15864 if (this.addicon) {
15865 this.editicon.setStyle('margin-left', '40px');
15867 this.editicon.on('click', function(e) {
15869 // we fire even if inothing is selected..
15870 this.fireEvent('edit', this, this.lastData );
15876 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15877 "up" : function(e){
15878 this.inKeyMode = true;
15882 "down" : function(e){
15883 if(!this.isExpanded()){
15884 this.onTriggerClick();
15886 this.inKeyMode = true;
15891 "enter" : function(e){
15892 // this.onViewClick();
15896 if(this.fireEvent("specialkey", this, e)){
15897 this.onViewClick(false);
15903 "esc" : function(e){
15907 "tab" : function(e){
15910 if(this.fireEvent("specialkey", this, e)){
15911 this.onViewClick(false);
15919 doRelay : function(foo, bar, hname){
15920 if(hname == 'down' || this.scope.isExpanded()){
15921 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15930 this.queryDelay = Math.max(this.queryDelay || 10,
15931 this.mode == 'local' ? 10 : 250);
15934 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15936 if(this.typeAhead){
15937 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15939 if(this.editable !== false){
15940 this.inputEl().on("keyup", this.onKeyUp, this);
15942 if(this.forceSelection){
15943 this.inputEl().on('blur', this.doForce, this);
15947 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15948 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15952 initTickableEvents: function()
15956 if(this.hiddenName){
15958 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15960 this.hiddenField.dom.value =
15961 this.hiddenValue !== undefined ? this.hiddenValue :
15962 this.value !== undefined ? this.value : '';
15964 // prevent input submission
15965 this.el.dom.removeAttribute('name');
15966 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15971 // this.list = this.el.select('ul.dropdown-menu',true).first();
15973 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15974 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15975 if(this.triggerList){
15976 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15979 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15980 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15982 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15983 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15985 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15986 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15988 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15989 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15990 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15993 this.cancelBtn.hide();
15998 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15999 _this.list.setWidth(lw);
16002 this.list.on('mouseover', this.onViewOver, this);
16003 this.list.on('mousemove', this.onViewMove, this);
16005 this.list.on('scroll', this.onViewScroll, this);
16008 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16009 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16012 this.view = new Roo.View(this.list, this.tpl, {
16017 selectedClass: this.selectedClass
16020 //this.view.wrapEl.setDisplayed(false);
16021 this.view.on('click', this.onViewClick, this);
16025 this.store.on('beforeload', this.onBeforeLoad, this);
16026 this.store.on('load', this.onLoad, this);
16027 this.store.on('loadexception', this.onLoadException, this);
16030 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16031 "up" : function(e){
16032 this.inKeyMode = true;
16036 "down" : function(e){
16037 this.inKeyMode = true;
16041 "enter" : function(e){
16042 if(this.fireEvent("specialkey", this, e)){
16043 this.onViewClick(false);
16049 "esc" : function(e){
16050 this.onTickableFooterButtonClick(e, false, false);
16053 "tab" : function(e){
16054 this.fireEvent("specialkey", this, e);
16056 this.onTickableFooterButtonClick(e, false, false);
16063 doRelay : function(e, fn, key){
16064 if(this.scope.isExpanded()){
16065 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16074 this.queryDelay = Math.max(this.queryDelay || 10,
16075 this.mode == 'local' ? 10 : 250);
16078 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16080 if(this.typeAhead){
16081 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16084 if(this.editable !== false){
16085 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16088 this.indicator = this.indicatorEl();
16090 if(this.indicator){
16091 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16092 this.indicator.hide();
16097 onDestroy : function(){
16099 this.view.setStore(null);
16100 this.view.el.removeAllListeners();
16101 this.view.el.remove();
16102 this.view.purgeListeners();
16105 this.list.dom.innerHTML = '';
16109 this.store.un('beforeload', this.onBeforeLoad, this);
16110 this.store.un('load', this.onLoad, this);
16111 this.store.un('loadexception', this.onLoadException, this);
16113 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16117 fireKey : function(e){
16118 if(e.isNavKeyPress() && !this.list.isVisible()){
16119 this.fireEvent("specialkey", this, e);
16124 onResize: function(w, h)
16128 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16130 // if(typeof w != 'number'){
16131 // // we do not handle it!?!?
16134 // var tw = this.trigger.getWidth();
16135 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16136 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16138 // this.inputEl().setWidth( this.adjustWidth('input', x));
16140 // //this.trigger.setStyle('left', x+'px');
16142 // if(this.list && this.listWidth === undefined){
16143 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16144 // this.list.setWidth(lw);
16145 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16153 * Allow or prevent the user from directly editing the field text. If false is passed,
16154 * the user will only be able to select from the items defined in the dropdown list. This method
16155 * is the runtime equivalent of setting the 'editable' config option at config time.
16156 * @param {Boolean} value True to allow the user to directly edit the field text
16158 setEditable : function(value){
16159 if(value == this.editable){
16162 this.editable = value;
16164 this.inputEl().dom.setAttribute('readOnly', true);
16165 this.inputEl().on('mousedown', this.onTriggerClick, this);
16166 this.inputEl().addClass('x-combo-noedit');
16168 this.inputEl().dom.setAttribute('readOnly', false);
16169 this.inputEl().un('mousedown', this.onTriggerClick, this);
16170 this.inputEl().removeClass('x-combo-noedit');
16176 onBeforeLoad : function(combo,opts){
16177 if(!this.hasFocus){
16181 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16183 this.restrictHeight();
16184 this.selectedIndex = -1;
16188 onLoad : function(){
16190 this.hasQuery = false;
16192 if(!this.hasFocus){
16196 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16197 this.loading.hide();
16200 if(this.store.getCount() > 0){
16203 this.restrictHeight();
16204 if(this.lastQuery == this.allQuery){
16205 if(this.editable && !this.tickable){
16206 this.inputEl().dom.select();
16210 !this.selectByValue(this.value, true) &&
16213 !this.store.lastOptions ||
16214 typeof(this.store.lastOptions.add) == 'undefined' ||
16215 this.store.lastOptions.add != true
16218 this.select(0, true);
16221 if(this.autoFocus){
16224 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16225 this.taTask.delay(this.typeAheadDelay);
16229 this.onEmptyResults();
16235 onLoadException : function()
16237 this.hasQuery = false;
16239 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16240 this.loading.hide();
16243 if(this.tickable && this.editable){
16248 // only causes errors at present
16249 //Roo.log(this.store.reader.jsonData);
16250 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16252 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16258 onTypeAhead : function(){
16259 if(this.store.getCount() > 0){
16260 var r = this.store.getAt(0);
16261 var newValue = r.data[this.displayField];
16262 var len = newValue.length;
16263 var selStart = this.getRawValue().length;
16265 if(selStart != len){
16266 this.setRawValue(newValue);
16267 this.selectText(selStart, newValue.length);
16273 onSelect : function(record, index){
16275 if(this.fireEvent('beforeselect', this, record, index) !== false){
16277 this.setFromData(index > -1 ? record.data : false);
16280 this.fireEvent('select', this, record, index);
16285 * Returns the currently selected field value or empty string if no value is set.
16286 * @return {String} value The selected value
16288 getValue : function()
16290 if(Roo.isIOS && this.useNativeIOS){
16291 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16295 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16298 if(this.valueField){
16299 return typeof this.value != 'undefined' ? this.value : '';
16301 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16305 getRawValue : function()
16307 if(Roo.isIOS && this.useNativeIOS){
16308 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16311 var v = this.inputEl().getValue();
16317 * Clears any text/value currently set in the field
16319 clearValue : function(){
16321 if(this.hiddenField){
16322 this.hiddenField.dom.value = '';
16325 this.setRawValue('');
16326 this.lastSelectionText = '';
16327 this.lastData = false;
16329 var close = this.closeTriggerEl();
16340 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16341 * will be displayed in the field. If the value does not match the data value of an existing item,
16342 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16343 * Otherwise the field will be blank (although the value will still be set).
16344 * @param {String} value The value to match
16346 setValue : function(v)
16348 if(Roo.isIOS && this.useNativeIOS){
16349 this.setIOSValue(v);
16359 if(this.valueField){
16360 var r = this.findRecord(this.valueField, v);
16362 text = r.data[this.displayField];
16363 }else if(this.valueNotFoundText !== undefined){
16364 text = this.valueNotFoundText;
16367 this.lastSelectionText = text;
16368 if(this.hiddenField){
16369 this.hiddenField.dom.value = v;
16371 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16374 var close = this.closeTriggerEl();
16377 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16383 * @property {Object} the last set data for the element
16388 * Sets the value of the field based on a object which is related to the record format for the store.
16389 * @param {Object} value the value to set as. or false on reset?
16391 setFromData : function(o){
16398 var dv = ''; // display value
16399 var vv = ''; // value value..
16401 if (this.displayField) {
16402 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16404 // this is an error condition!!!
16405 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16408 if(this.valueField){
16409 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16412 var close = this.closeTriggerEl();
16415 if(dv.length || vv * 1 > 0){
16417 this.blockFocus=true;
16423 if(this.hiddenField){
16424 this.hiddenField.dom.value = vv;
16426 this.lastSelectionText = dv;
16427 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16431 // no hidden field.. - we store the value in 'value', but still display
16432 // display field!!!!
16433 this.lastSelectionText = dv;
16434 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16441 reset : function(){
16442 // overridden so that last data is reset..
16449 this.setValue(this.originalValue);
16450 //this.clearInvalid();
16451 this.lastData = false;
16453 this.view.clearSelections();
16459 findRecord : function(prop, value){
16461 if(this.store.getCount() > 0){
16462 this.store.each(function(r){
16463 if(r.data[prop] == value){
16473 getName: function()
16475 // returns hidden if it's set..
16476 if (!this.rendered) {return ''};
16477 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16481 onViewMove : function(e, t){
16482 this.inKeyMode = false;
16486 onViewOver : function(e, t){
16487 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16490 var item = this.view.findItemFromChild(t);
16493 var index = this.view.indexOf(item);
16494 this.select(index, false);
16499 onViewClick : function(view, doFocus, el, e)
16501 var index = this.view.getSelectedIndexes()[0];
16503 var r = this.store.getAt(index);
16507 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16514 Roo.each(this.tickItems, function(v,k){
16516 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16518 _this.tickItems.splice(k, 1);
16520 if(typeof(e) == 'undefined' && view == false){
16521 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16533 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16534 this.tickItems.push(r.data);
16537 if(typeof(e) == 'undefined' && view == false){
16538 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16545 this.onSelect(r, index);
16547 if(doFocus !== false && !this.blockFocus){
16548 this.inputEl().focus();
16553 restrictHeight : function(){
16554 //this.innerList.dom.style.height = '';
16555 //var inner = this.innerList.dom;
16556 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16557 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16558 //this.list.beginUpdate();
16559 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16560 this.list.alignTo(this.inputEl(), this.listAlign);
16561 this.list.alignTo(this.inputEl(), this.listAlign);
16562 //this.list.endUpdate();
16566 onEmptyResults : function(){
16568 if(this.tickable && this.editable){
16569 this.hasFocus = false;
16570 this.restrictHeight();
16578 * Returns true if the dropdown list is expanded, else false.
16580 isExpanded : function(){
16581 return this.list.isVisible();
16585 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16586 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16587 * @param {String} value The data value of the item to select
16588 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16589 * selected item if it is not currently in view (defaults to true)
16590 * @return {Boolean} True if the value matched an item in the list, else false
16592 selectByValue : function(v, scrollIntoView){
16593 if(v !== undefined && v !== null){
16594 var r = this.findRecord(this.valueField || this.displayField, v);
16596 this.select(this.store.indexOf(r), scrollIntoView);
16604 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16605 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16606 * @param {Number} index The zero-based index of the list item to select
16607 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16608 * selected item if it is not currently in view (defaults to true)
16610 select : function(index, scrollIntoView){
16611 this.selectedIndex = index;
16612 this.view.select(index);
16613 if(scrollIntoView !== false){
16614 var el = this.view.getNode(index);
16616 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16619 this.list.scrollChildIntoView(el, false);
16625 selectNext : function(){
16626 var ct = this.store.getCount();
16628 if(this.selectedIndex == -1){
16630 }else if(this.selectedIndex < ct-1){
16631 this.select(this.selectedIndex+1);
16637 selectPrev : function(){
16638 var ct = this.store.getCount();
16640 if(this.selectedIndex == -1){
16642 }else if(this.selectedIndex != 0){
16643 this.select(this.selectedIndex-1);
16649 onKeyUp : function(e){
16650 if(this.editable !== false && !e.isSpecialKey()){
16651 this.lastKey = e.getKey();
16652 this.dqTask.delay(this.queryDelay);
16657 validateBlur : function(){
16658 return !this.list || !this.list.isVisible();
16662 initQuery : function(){
16664 var v = this.getRawValue();
16666 if(this.tickable && this.editable){
16667 v = this.tickableInputEl().getValue();
16674 doForce : function(){
16675 if(this.inputEl().dom.value.length > 0){
16676 this.inputEl().dom.value =
16677 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16683 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16684 * query allowing the query action to be canceled if needed.
16685 * @param {String} query The SQL query to execute
16686 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16687 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16688 * saved in the current store (defaults to false)
16690 doQuery : function(q, forceAll){
16692 if(q === undefined || q === null){
16697 forceAll: forceAll,
16701 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16706 forceAll = qe.forceAll;
16707 if(forceAll === true || (q.length >= this.minChars)){
16709 this.hasQuery = true;
16711 if(this.lastQuery != q || this.alwaysQuery){
16712 this.lastQuery = q;
16713 if(this.mode == 'local'){
16714 this.selectedIndex = -1;
16716 this.store.clearFilter();
16719 if(this.specialFilter){
16720 this.fireEvent('specialfilter', this);
16725 this.store.filter(this.displayField, q);
16728 this.store.fireEvent("datachanged", this.store);
16735 this.store.baseParams[this.queryParam] = q;
16737 var options = {params : this.getParams(q)};
16740 options.add = true;
16741 options.params.start = this.page * this.pageSize;
16744 this.store.load(options);
16747 * this code will make the page width larger, at the beginning, the list not align correctly,
16748 * we should expand the list on onLoad
16749 * so command out it
16754 this.selectedIndex = -1;
16759 this.loadNext = false;
16763 getParams : function(q){
16765 //p[this.queryParam] = q;
16769 p.limit = this.pageSize;
16775 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16777 collapse : function(){
16778 if(!this.isExpanded()){
16784 this.hasFocus = false;
16788 this.cancelBtn.hide();
16789 this.trigger.show();
16792 this.tickableInputEl().dom.value = '';
16793 this.tickableInputEl().blur();
16798 Roo.get(document).un('mousedown', this.collapseIf, this);
16799 Roo.get(document).un('mousewheel', this.collapseIf, this);
16800 if (!this.editable) {
16801 Roo.get(document).un('keydown', this.listKeyPress, this);
16803 this.fireEvent('collapse', this);
16809 collapseIf : function(e){
16810 var in_combo = e.within(this.el);
16811 var in_list = e.within(this.list);
16812 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16814 if (in_combo || in_list || is_list) {
16815 //e.stopPropagation();
16820 this.onTickableFooterButtonClick(e, false, false);
16828 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16830 expand : function(){
16832 if(this.isExpanded() || !this.hasFocus){
16836 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16837 this.list.setWidth(lw);
16843 this.restrictHeight();
16847 this.tickItems = Roo.apply([], this.item);
16850 this.cancelBtn.show();
16851 this.trigger.hide();
16854 this.tickableInputEl().focus();
16859 Roo.get(document).on('mousedown', this.collapseIf, this);
16860 Roo.get(document).on('mousewheel', this.collapseIf, this);
16861 if (!this.editable) {
16862 Roo.get(document).on('keydown', this.listKeyPress, this);
16865 this.fireEvent('expand', this);
16869 // Implements the default empty TriggerField.onTriggerClick function
16870 onTriggerClick : function(e)
16872 Roo.log('trigger click');
16874 if(this.disabled || !this.triggerList){
16879 this.loadNext = false;
16881 if(this.isExpanded()){
16883 if (!this.blockFocus) {
16884 this.inputEl().focus();
16888 this.hasFocus = true;
16889 if(this.triggerAction == 'all') {
16890 this.doQuery(this.allQuery, true);
16892 this.doQuery(this.getRawValue());
16894 if (!this.blockFocus) {
16895 this.inputEl().focus();
16900 onTickableTriggerClick : function(e)
16907 this.loadNext = false;
16908 this.hasFocus = true;
16910 if(this.triggerAction == 'all') {
16911 this.doQuery(this.allQuery, true);
16913 this.doQuery(this.getRawValue());
16917 onSearchFieldClick : function(e)
16919 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16920 this.onTickableFooterButtonClick(e, false, false);
16924 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16929 this.loadNext = false;
16930 this.hasFocus = true;
16932 if(this.triggerAction == 'all') {
16933 this.doQuery(this.allQuery, true);
16935 this.doQuery(this.getRawValue());
16939 listKeyPress : function(e)
16941 //Roo.log('listkeypress');
16942 // scroll to first matching element based on key pres..
16943 if (e.isSpecialKey()) {
16946 var k = String.fromCharCode(e.getKey()).toUpperCase();
16949 var csel = this.view.getSelectedNodes();
16950 var cselitem = false;
16952 var ix = this.view.indexOf(csel[0]);
16953 cselitem = this.store.getAt(ix);
16954 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16960 this.store.each(function(v) {
16962 // start at existing selection.
16963 if (cselitem.id == v.id) {
16969 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16970 match = this.store.indexOf(v);
16976 if (match === false) {
16977 return true; // no more action?
16980 this.view.select(match);
16981 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16982 sn.scrollIntoView(sn.dom.parentNode, false);
16985 onViewScroll : function(e, t){
16987 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){
16991 this.hasQuery = true;
16993 this.loading = this.list.select('.loading', true).first();
16995 if(this.loading === null){
16996 this.list.createChild({
16998 cls: 'loading roo-select2-more-results roo-select2-active',
16999 html: 'Loading more results...'
17002 this.loading = this.list.select('.loading', true).first();
17004 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17006 this.loading.hide();
17009 this.loading.show();
17014 this.loadNext = true;
17016 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17021 addItem : function(o)
17023 var dv = ''; // display value
17025 if (this.displayField) {
17026 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17028 // this is an error condition!!!
17029 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17036 var choice = this.choices.createChild({
17038 cls: 'roo-select2-search-choice',
17047 cls: 'roo-select2-search-choice-close fa fa-times',
17052 }, this.searchField);
17054 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17056 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17064 this.inputEl().dom.value = '';
17069 onRemoveItem : function(e, _self, o)
17071 e.preventDefault();
17073 this.lastItem = Roo.apply([], this.item);
17075 var index = this.item.indexOf(o.data) * 1;
17078 Roo.log('not this item?!');
17082 this.item.splice(index, 1);
17087 this.fireEvent('remove', this, e);
17093 syncValue : function()
17095 if(!this.item.length){
17102 Roo.each(this.item, function(i){
17103 if(_this.valueField){
17104 value.push(i[_this.valueField]);
17111 this.value = value.join(',');
17113 if(this.hiddenField){
17114 this.hiddenField.dom.value = this.value;
17117 this.store.fireEvent("datachanged", this.store);
17122 clearItem : function()
17124 if(!this.multiple){
17130 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17138 if(this.tickable && !Roo.isTouch){
17139 this.view.refresh();
17143 inputEl: function ()
17145 if(Roo.isIOS && this.useNativeIOS){
17146 return this.el.select('select.roo-ios-select', true).first();
17149 if(Roo.isTouch && this.mobileTouchView){
17150 return this.el.select('input.form-control',true).first();
17154 return this.searchField;
17157 return this.el.select('input.form-control',true).first();
17160 onTickableFooterButtonClick : function(e, btn, el)
17162 e.preventDefault();
17164 this.lastItem = Roo.apply([], this.item);
17166 if(btn && btn.name == 'cancel'){
17167 this.tickItems = Roo.apply([], this.item);
17176 Roo.each(this.tickItems, function(o){
17184 validate : function()
17186 if(this.getVisibilityEl().hasClass('hidden')){
17190 var v = this.getRawValue();
17193 v = this.getValue();
17196 if(this.disabled || this.allowBlank || v.length){
17201 this.markInvalid();
17205 tickableInputEl : function()
17207 if(!this.tickable || !this.editable){
17208 return this.inputEl();
17211 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17215 getAutoCreateTouchView : function()
17220 cls: 'form-group' //input-group
17226 type : this.inputType,
17227 cls : 'form-control x-combo-noedit',
17228 autocomplete: 'new-password',
17229 placeholder : this.placeholder || '',
17234 input.name = this.name;
17238 input.cls += ' input-' + this.size;
17241 if (this.disabled) {
17242 input.disabled = true;
17246 cls : 'roo-combobox-wrap',
17253 inputblock.cls += ' input-group';
17255 inputblock.cn.unshift({
17257 cls : 'input-group-addon input-group-prepend input-group-text',
17262 if(this.removable && !this.multiple){
17263 inputblock.cls += ' roo-removable';
17265 inputblock.cn.push({
17268 cls : 'roo-combo-removable-btn close'
17272 if(this.hasFeedback && !this.allowBlank){
17274 inputblock.cls += ' has-feedback';
17276 inputblock.cn.push({
17278 cls: 'glyphicon form-control-feedback'
17285 inputblock.cls += (this.before) ? '' : ' input-group';
17287 inputblock.cn.push({
17289 cls : 'input-group-addon input-group-append input-group-text',
17295 var ibwrap = inputblock;
17300 cls: 'roo-select2-choices',
17304 cls: 'roo-select2-search-field',
17317 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17322 cls: 'form-hidden-field'
17328 if(!this.multiple && this.showToggleBtn){
17334 if (this.caret != false) {
17337 cls: 'fa fa-' + this.caret
17344 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17346 Roo.bootstrap.version == 3 ? caret : '',
17349 cls: 'combobox-clear',
17363 combobox.cls += ' roo-select2-container-multi';
17366 var align = this.labelAlign || this.parentLabelAlign();
17368 if (align ==='left' && this.fieldLabel.length) {
17373 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17374 tooltip : 'This field is required'
17378 cls : 'control-label col-form-label',
17379 html : this.fieldLabel
17383 cls : 'roo-combobox-wrap ',
17390 var labelCfg = cfg.cn[1];
17391 var contentCfg = cfg.cn[2];
17394 if(this.indicatorpos == 'right'){
17399 cls : 'control-label col-form-label',
17403 html : this.fieldLabel
17407 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17408 tooltip : 'This field is required'
17413 cls : "roo-combobox-wrap ",
17421 labelCfg = cfg.cn[0];
17422 contentCfg = cfg.cn[1];
17427 if(this.labelWidth > 12){
17428 labelCfg.style = "width: " + this.labelWidth + 'px';
17431 if(this.labelWidth < 13 && this.labelmd == 0){
17432 this.labelmd = this.labelWidth;
17435 if(this.labellg > 0){
17436 labelCfg.cls += ' col-lg-' + this.labellg;
17437 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17440 if(this.labelmd > 0){
17441 labelCfg.cls += ' col-md-' + this.labelmd;
17442 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17445 if(this.labelsm > 0){
17446 labelCfg.cls += ' col-sm-' + this.labelsm;
17447 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17450 if(this.labelxs > 0){
17451 labelCfg.cls += ' col-xs-' + this.labelxs;
17452 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17456 } else if ( this.fieldLabel.length) {
17460 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17461 tooltip : 'This field is required'
17465 cls : 'control-label',
17466 html : this.fieldLabel
17477 if(this.indicatorpos == 'right'){
17481 cls : 'control-label',
17482 html : this.fieldLabel,
17486 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17487 tooltip : 'This field is required'
17504 var settings = this;
17506 ['xs','sm','md','lg'].map(function(size){
17507 if (settings[size]) {
17508 cfg.cls += ' col-' + size + '-' + settings[size];
17515 initTouchView : function()
17517 this.renderTouchView();
17519 this.touchViewEl.on('scroll', function(){
17520 this.el.dom.scrollTop = 0;
17523 this.originalValue = this.getValue();
17525 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17527 this.inputEl().on("click", this.showTouchView, this);
17528 if (this.triggerEl) {
17529 this.triggerEl.on("click", this.showTouchView, this);
17533 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17534 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17536 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17538 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17539 this.store.on('load', this.onTouchViewLoad, this);
17540 this.store.on('loadexception', this.onTouchViewLoadException, this);
17542 if(this.hiddenName){
17544 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17546 this.hiddenField.dom.value =
17547 this.hiddenValue !== undefined ? this.hiddenValue :
17548 this.value !== undefined ? this.value : '';
17550 this.el.dom.removeAttribute('name');
17551 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17555 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17556 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17559 if(this.removable && !this.multiple){
17560 var close = this.closeTriggerEl();
17562 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17563 close.on('click', this.removeBtnClick, this, close);
17567 * fix the bug in Safari iOS8
17569 this.inputEl().on("focus", function(e){
17570 document.activeElement.blur();
17573 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17580 renderTouchView : function()
17582 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17583 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17585 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17586 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17588 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17589 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17590 this.touchViewBodyEl.setStyle('overflow', 'auto');
17592 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17593 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17595 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17596 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17600 showTouchView : function()
17606 this.touchViewHeaderEl.hide();
17608 if(this.modalTitle.length){
17609 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17610 this.touchViewHeaderEl.show();
17613 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17614 this.touchViewEl.show();
17616 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17618 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17619 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17621 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17623 if(this.modalTitle.length){
17624 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17627 this.touchViewBodyEl.setHeight(bodyHeight);
17631 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17633 this.touchViewEl.addClass(['in','show']);
17636 if(this._touchViewMask){
17637 Roo.get(document.body).addClass("x-body-masked");
17638 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17639 this._touchViewMask.setStyle('z-index', 10000);
17640 this._touchViewMask.addClass('show');
17643 this.doTouchViewQuery();
17647 hideTouchView : function()
17649 this.touchViewEl.removeClass(['in','show']);
17653 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17655 this.touchViewEl.setStyle('display', 'none');
17658 if(this._touchViewMask){
17659 this._touchViewMask.removeClass('show');
17660 Roo.get(document.body).removeClass("x-body-masked");
17664 setTouchViewValue : function()
17671 Roo.each(this.tickItems, function(o){
17676 this.hideTouchView();
17679 doTouchViewQuery : function()
17688 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17692 if(!this.alwaysQuery || this.mode == 'local'){
17693 this.onTouchViewLoad();
17700 onTouchViewBeforeLoad : function(combo,opts)
17706 onTouchViewLoad : function()
17708 if(this.store.getCount() < 1){
17709 this.onTouchViewEmptyResults();
17713 this.clearTouchView();
17715 var rawValue = this.getRawValue();
17717 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17719 this.tickItems = [];
17721 this.store.data.each(function(d, rowIndex){
17722 var row = this.touchViewListGroup.createChild(template);
17724 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17725 row.addClass(d.data.cls);
17728 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17731 html : d.data[this.displayField]
17734 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17735 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17738 row.removeClass('selected');
17739 if(!this.multiple && this.valueField &&
17740 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17743 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17744 row.addClass('selected');
17747 if(this.multiple && this.valueField &&
17748 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17752 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17753 this.tickItems.push(d.data);
17756 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17760 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17762 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17764 if(this.modalTitle.length){
17765 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17768 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17770 if(this.mobile_restrict_height && listHeight < bodyHeight){
17771 this.touchViewBodyEl.setHeight(listHeight);
17776 if(firstChecked && listHeight > bodyHeight){
17777 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17782 onTouchViewLoadException : function()
17784 this.hideTouchView();
17787 onTouchViewEmptyResults : function()
17789 this.clearTouchView();
17791 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17793 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17797 clearTouchView : function()
17799 this.touchViewListGroup.dom.innerHTML = '';
17802 onTouchViewClick : function(e, el, o)
17804 e.preventDefault();
17807 var rowIndex = o.rowIndex;
17809 var r = this.store.getAt(rowIndex);
17811 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17813 if(!this.multiple){
17814 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17815 c.dom.removeAttribute('checked');
17818 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17820 this.setFromData(r.data);
17822 var close = this.closeTriggerEl();
17828 this.hideTouchView();
17830 this.fireEvent('select', this, r, rowIndex);
17835 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17836 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17837 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17841 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17842 this.addItem(r.data);
17843 this.tickItems.push(r.data);
17847 getAutoCreateNativeIOS : function()
17850 cls: 'form-group' //input-group,
17855 cls : 'roo-ios-select'
17859 combobox.name = this.name;
17862 if (this.disabled) {
17863 combobox.disabled = true;
17866 var settings = this;
17868 ['xs','sm','md','lg'].map(function(size){
17869 if (settings[size]) {
17870 cfg.cls += ' col-' + size + '-' + settings[size];
17880 initIOSView : function()
17882 this.store.on('load', this.onIOSViewLoad, this);
17887 onIOSViewLoad : function()
17889 if(this.store.getCount() < 1){
17893 this.clearIOSView();
17895 if(this.allowBlank) {
17897 var default_text = '-- SELECT --';
17899 if(this.placeholder.length){
17900 default_text = this.placeholder;
17903 if(this.emptyTitle.length){
17904 default_text += ' - ' + this.emptyTitle + ' -';
17907 var opt = this.inputEl().createChild({
17910 html : default_text
17914 o[this.valueField] = 0;
17915 o[this.displayField] = default_text;
17917 this.ios_options.push({
17924 this.store.data.each(function(d, rowIndex){
17928 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17929 html = d.data[this.displayField];
17934 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17935 value = d.data[this.valueField];
17944 if(this.value == d.data[this.valueField]){
17945 option['selected'] = true;
17948 var opt = this.inputEl().createChild(option);
17950 this.ios_options.push({
17957 this.inputEl().on('change', function(){
17958 this.fireEvent('select', this);
17963 clearIOSView: function()
17965 this.inputEl().dom.innerHTML = '';
17967 this.ios_options = [];
17970 setIOSValue: function(v)
17974 if(!this.ios_options){
17978 Roo.each(this.ios_options, function(opts){
17980 opts.el.dom.removeAttribute('selected');
17982 if(opts.data[this.valueField] != v){
17986 opts.el.dom.setAttribute('selected', true);
17992 * @cfg {Boolean} grow
17996 * @cfg {Number} growMin
18000 * @cfg {Number} growMax
18009 Roo.apply(Roo.bootstrap.ComboBox, {
18013 cls: 'modal-header',
18035 cls: 'list-group-item',
18039 cls: 'roo-combobox-list-group-item-value'
18043 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18057 listItemCheckbox : {
18059 cls: 'list-group-item',
18063 cls: 'roo-combobox-list-group-item-value'
18067 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18083 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18088 cls: 'modal-footer',
18096 cls: 'col-xs-6 text-left',
18099 cls: 'btn btn-danger roo-touch-view-cancel',
18105 cls: 'col-xs-6 text-right',
18108 cls: 'btn btn-success roo-touch-view-ok',
18119 Roo.apply(Roo.bootstrap.ComboBox, {
18121 touchViewTemplate : {
18123 cls: 'modal fade roo-combobox-touch-view',
18127 cls: 'modal-dialog',
18128 style : 'position:fixed', // we have to fix position....
18132 cls: 'modal-content',
18134 Roo.bootstrap.ComboBox.header,
18135 Roo.bootstrap.ComboBox.body,
18136 Roo.bootstrap.ComboBox.footer
18145 * Ext JS Library 1.1.1
18146 * Copyright(c) 2006-2007, Ext JS, LLC.
18148 * Originally Released Under LGPL - original licence link has changed is not relivant.
18151 * <script type="text/javascript">
18156 * @extends Roo.util.Observable
18157 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18158 * This class also supports single and multi selection modes. <br>
18159 * Create a data model bound view:
18161 var store = new Roo.data.Store(...);
18163 var view = new Roo.View({
18165 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18167 singleSelect: true,
18168 selectedClass: "ydataview-selected",
18172 // listen for node click?
18173 view.on("click", function(vw, index, node, e){
18174 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18178 dataModel.load("foobar.xml");
18180 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18182 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18183 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18185 * Note: old style constructor is still suported (container, template, config)
18188 * Create a new View
18189 * @param {Object} config The config object
18192 Roo.View = function(config, depreciated_tpl, depreciated_config){
18194 this.parent = false;
18196 if (typeof(depreciated_tpl) == 'undefined') {
18197 // new way.. - universal constructor.
18198 Roo.apply(this, config);
18199 this.el = Roo.get(this.el);
18202 this.el = Roo.get(config);
18203 this.tpl = depreciated_tpl;
18204 Roo.apply(this, depreciated_config);
18206 this.wrapEl = this.el.wrap().wrap();
18207 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18210 if(typeof(this.tpl) == "string"){
18211 this.tpl = new Roo.Template(this.tpl);
18213 // support xtype ctors..
18214 this.tpl = new Roo.factory(this.tpl, Roo);
18218 this.tpl.compile();
18223 * @event beforeclick
18224 * Fires before a click is processed. Returns false to cancel the default action.
18225 * @param {Roo.View} this
18226 * @param {Number} index The index of the target node
18227 * @param {HTMLElement} node The target node
18228 * @param {Roo.EventObject} e The raw event object
18230 "beforeclick" : true,
18233 * Fires when a template node is clicked.
18234 * @param {Roo.View} this
18235 * @param {Number} index The index of the target node
18236 * @param {HTMLElement} node The target node
18237 * @param {Roo.EventObject} e The raw event object
18242 * Fires when a template node is double clicked.
18243 * @param {Roo.View} this
18244 * @param {Number} index The index of the target node
18245 * @param {HTMLElement} node The target node
18246 * @param {Roo.EventObject} e The raw event object
18250 * @event contextmenu
18251 * Fires when a template node is right clicked.
18252 * @param {Roo.View} this
18253 * @param {Number} index The index of the target node
18254 * @param {HTMLElement} node The target node
18255 * @param {Roo.EventObject} e The raw event object
18257 "contextmenu" : true,
18259 * @event selectionchange
18260 * Fires when the selected nodes change.
18261 * @param {Roo.View} this
18262 * @param {Array} selections Array of the selected nodes
18264 "selectionchange" : true,
18267 * @event beforeselect
18268 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18269 * @param {Roo.View} this
18270 * @param {HTMLElement} node The node to be selected
18271 * @param {Array} selections Array of currently selected nodes
18273 "beforeselect" : true,
18275 * @event preparedata
18276 * Fires on every row to render, to allow you to change the data.
18277 * @param {Roo.View} this
18278 * @param {Object} data to be rendered (change this)
18280 "preparedata" : true
18288 "click": this.onClick,
18289 "dblclick": this.onDblClick,
18290 "contextmenu": this.onContextMenu,
18294 this.selections = [];
18296 this.cmp = new Roo.CompositeElementLite([]);
18298 this.store = Roo.factory(this.store, Roo.data);
18299 this.setStore(this.store, true);
18302 if ( this.footer && this.footer.xtype) {
18304 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18306 this.footer.dataSource = this.store;
18307 this.footer.container = fctr;
18308 this.footer = Roo.factory(this.footer, Roo);
18309 fctr.insertFirst(this.el);
18311 // this is a bit insane - as the paging toolbar seems to detach the el..
18312 // dom.parentNode.parentNode.parentNode
18313 // they get detached?
18317 Roo.View.superclass.constructor.call(this);
18322 Roo.extend(Roo.View, Roo.util.Observable, {
18325 * @cfg {Roo.data.Store} store Data store to load data from.
18330 * @cfg {String|Roo.Element} el The container element.
18335 * @cfg {String|Roo.Template} tpl The template used by this View
18339 * @cfg {String} dataName the named area of the template to use as the data area
18340 * Works with domtemplates roo-name="name"
18344 * @cfg {String} selectedClass The css class to add to selected nodes
18346 selectedClass : "x-view-selected",
18348 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18353 * @cfg {String} text to display on mask (default Loading)
18357 * @cfg {Boolean} multiSelect Allow multiple selection
18359 multiSelect : false,
18361 * @cfg {Boolean} singleSelect Allow single selection
18363 singleSelect: false,
18366 * @cfg {Boolean} toggleSelect - selecting
18368 toggleSelect : false,
18371 * @cfg {Boolean} tickable - selecting
18376 * Returns the element this view is bound to.
18377 * @return {Roo.Element}
18379 getEl : function(){
18380 return this.wrapEl;
18386 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18388 refresh : function(){
18389 //Roo.log('refresh');
18392 // if we are using something like 'domtemplate', then
18393 // the what gets used is:
18394 // t.applySubtemplate(NAME, data, wrapping data..)
18395 // the outer template then get' applied with
18396 // the store 'extra data'
18397 // and the body get's added to the
18398 // roo-name="data" node?
18399 // <span class='roo-tpl-{name}'></span> ?????
18403 this.clearSelections();
18404 this.el.update("");
18406 var records = this.store.getRange();
18407 if(records.length < 1) {
18409 // is this valid?? = should it render a template??
18411 this.el.update(this.emptyText);
18415 if (this.dataName) {
18416 this.el.update(t.apply(this.store.meta)); //????
18417 el = this.el.child('.roo-tpl-' + this.dataName);
18420 for(var i = 0, len = records.length; i < len; i++){
18421 var data = this.prepareData(records[i].data, i, records[i]);
18422 this.fireEvent("preparedata", this, data, i, records[i]);
18424 var d = Roo.apply({}, data);
18427 Roo.apply(d, {'roo-id' : Roo.id()});
18431 Roo.each(this.parent.item, function(item){
18432 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18435 Roo.apply(d, {'roo-data-checked' : 'checked'});
18439 html[html.length] = Roo.util.Format.trim(
18441 t.applySubtemplate(this.dataName, d, this.store.meta) :
18448 el.update(html.join(""));
18449 this.nodes = el.dom.childNodes;
18450 this.updateIndexes(0);
18455 * Function to override to reformat the data that is sent to
18456 * the template for each node.
18457 * DEPRICATED - use the preparedata event handler.
18458 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18459 * a JSON object for an UpdateManager bound view).
18461 prepareData : function(data, index, record)
18463 this.fireEvent("preparedata", this, data, index, record);
18467 onUpdate : function(ds, record){
18468 // Roo.log('on update');
18469 this.clearSelections();
18470 var index = this.store.indexOf(record);
18471 var n = this.nodes[index];
18472 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18473 n.parentNode.removeChild(n);
18474 this.updateIndexes(index, index);
18480 onAdd : function(ds, records, index)
18482 //Roo.log(['on Add', ds, records, index] );
18483 this.clearSelections();
18484 if(this.nodes.length == 0){
18488 var n = this.nodes[index];
18489 for(var i = 0, len = records.length; i < len; i++){
18490 var d = this.prepareData(records[i].data, i, records[i]);
18492 this.tpl.insertBefore(n, d);
18495 this.tpl.append(this.el, d);
18498 this.updateIndexes(index);
18501 onRemove : function(ds, record, index){
18502 // Roo.log('onRemove');
18503 this.clearSelections();
18504 var el = this.dataName ?
18505 this.el.child('.roo-tpl-' + this.dataName) :
18508 el.dom.removeChild(this.nodes[index]);
18509 this.updateIndexes(index);
18513 * Refresh an individual node.
18514 * @param {Number} index
18516 refreshNode : function(index){
18517 this.onUpdate(this.store, this.store.getAt(index));
18520 updateIndexes : function(startIndex, endIndex){
18521 var ns = this.nodes;
18522 startIndex = startIndex || 0;
18523 endIndex = endIndex || ns.length - 1;
18524 for(var i = startIndex; i <= endIndex; i++){
18525 ns[i].nodeIndex = i;
18530 * Changes the data store this view uses and refresh the view.
18531 * @param {Store} store
18533 setStore : function(store, initial){
18534 if(!initial && this.store){
18535 this.store.un("datachanged", this.refresh);
18536 this.store.un("add", this.onAdd);
18537 this.store.un("remove", this.onRemove);
18538 this.store.un("update", this.onUpdate);
18539 this.store.un("clear", this.refresh);
18540 this.store.un("beforeload", this.onBeforeLoad);
18541 this.store.un("load", this.onLoad);
18542 this.store.un("loadexception", this.onLoad);
18546 store.on("datachanged", this.refresh, this);
18547 store.on("add", this.onAdd, this);
18548 store.on("remove", this.onRemove, this);
18549 store.on("update", this.onUpdate, this);
18550 store.on("clear", this.refresh, this);
18551 store.on("beforeload", this.onBeforeLoad, this);
18552 store.on("load", this.onLoad, this);
18553 store.on("loadexception", this.onLoad, this);
18561 * onbeforeLoad - masks the loading area.
18564 onBeforeLoad : function(store,opts)
18566 //Roo.log('onBeforeLoad');
18568 this.el.update("");
18570 this.el.mask(this.mask ? this.mask : "Loading" );
18572 onLoad : function ()
18579 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18580 * @param {HTMLElement} node
18581 * @return {HTMLElement} The template node
18583 findItemFromChild : function(node){
18584 var el = this.dataName ?
18585 this.el.child('.roo-tpl-' + this.dataName,true) :
18588 if(!node || node.parentNode == el){
18591 var p = node.parentNode;
18592 while(p && p != el){
18593 if(p.parentNode == el){
18602 onClick : function(e){
18603 var item = this.findItemFromChild(e.getTarget());
18605 var index = this.indexOf(item);
18606 if(this.onItemClick(item, index, e) !== false){
18607 this.fireEvent("click", this, index, item, e);
18610 this.clearSelections();
18615 onContextMenu : function(e){
18616 var item = this.findItemFromChild(e.getTarget());
18618 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18623 onDblClick : function(e){
18624 var item = this.findItemFromChild(e.getTarget());
18626 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18630 onItemClick : function(item, index, e)
18632 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18635 if (this.toggleSelect) {
18636 var m = this.isSelected(item) ? 'unselect' : 'select';
18639 _t[m](item, true, false);
18642 if(this.multiSelect || this.singleSelect){
18643 if(this.multiSelect && e.shiftKey && this.lastSelection){
18644 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18646 this.select(item, this.multiSelect && e.ctrlKey);
18647 this.lastSelection = item;
18650 if(!this.tickable){
18651 e.preventDefault();
18659 * Get the number of selected nodes.
18662 getSelectionCount : function(){
18663 return this.selections.length;
18667 * Get the currently selected nodes.
18668 * @return {Array} An array of HTMLElements
18670 getSelectedNodes : function(){
18671 return this.selections;
18675 * Get the indexes of the selected nodes.
18678 getSelectedIndexes : function(){
18679 var indexes = [], s = this.selections;
18680 for(var i = 0, len = s.length; i < len; i++){
18681 indexes.push(s[i].nodeIndex);
18687 * Clear all selections
18688 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18690 clearSelections : function(suppressEvent){
18691 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18692 this.cmp.elements = this.selections;
18693 this.cmp.removeClass(this.selectedClass);
18694 this.selections = [];
18695 if(!suppressEvent){
18696 this.fireEvent("selectionchange", this, this.selections);
18702 * Returns true if the passed node is selected
18703 * @param {HTMLElement/Number} node The node or node index
18704 * @return {Boolean}
18706 isSelected : function(node){
18707 var s = this.selections;
18711 node = this.getNode(node);
18712 return s.indexOf(node) !== -1;
18717 * @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
18718 * @param {Boolean} keepExisting (optional) true to keep existing selections
18719 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18721 select : function(nodeInfo, keepExisting, suppressEvent){
18722 if(nodeInfo instanceof Array){
18724 this.clearSelections(true);
18726 for(var i = 0, len = nodeInfo.length; i < len; i++){
18727 this.select(nodeInfo[i], true, true);
18731 var node = this.getNode(nodeInfo);
18732 if(!node || this.isSelected(node)){
18733 return; // already selected.
18736 this.clearSelections(true);
18739 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18740 Roo.fly(node).addClass(this.selectedClass);
18741 this.selections.push(node);
18742 if(!suppressEvent){
18743 this.fireEvent("selectionchange", this, this.selections);
18751 * @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
18752 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18753 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18755 unselect : function(nodeInfo, keepExisting, suppressEvent)
18757 if(nodeInfo instanceof Array){
18758 Roo.each(this.selections, function(s) {
18759 this.unselect(s, nodeInfo);
18763 var node = this.getNode(nodeInfo);
18764 if(!node || !this.isSelected(node)){
18765 //Roo.log("not selected");
18766 return; // not selected.
18770 Roo.each(this.selections, function(s) {
18772 Roo.fly(node).removeClass(this.selectedClass);
18779 this.selections= ns;
18780 this.fireEvent("selectionchange", this, this.selections);
18784 * Gets a template node.
18785 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18786 * @return {HTMLElement} The node or null if it wasn't found
18788 getNode : function(nodeInfo){
18789 if(typeof nodeInfo == "string"){
18790 return document.getElementById(nodeInfo);
18791 }else if(typeof nodeInfo == "number"){
18792 return this.nodes[nodeInfo];
18798 * Gets a range template nodes.
18799 * @param {Number} startIndex
18800 * @param {Number} endIndex
18801 * @return {Array} An array of nodes
18803 getNodes : function(start, end){
18804 var ns = this.nodes;
18805 start = start || 0;
18806 end = typeof end == "undefined" ? ns.length - 1 : end;
18809 for(var i = start; i <= end; i++){
18813 for(var i = start; i >= end; i--){
18821 * Finds the index of the passed node
18822 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18823 * @return {Number} The index of the node or -1
18825 indexOf : function(node){
18826 node = this.getNode(node);
18827 if(typeof node.nodeIndex == "number"){
18828 return node.nodeIndex;
18830 var ns = this.nodes;
18831 for(var i = 0, len = ns.length; i < len; i++){
18842 * based on jquery fullcalendar
18846 Roo.bootstrap = Roo.bootstrap || {};
18848 * @class Roo.bootstrap.Calendar
18849 * @extends Roo.bootstrap.Component
18850 * Bootstrap Calendar class
18851 * @cfg {Boolean} loadMask (true|false) default false
18852 * @cfg {Object} header generate the user specific header of the calendar, default false
18855 * Create a new Container
18856 * @param {Object} config The config object
18861 Roo.bootstrap.Calendar = function(config){
18862 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18866 * Fires when a date is selected
18867 * @param {DatePicker} this
18868 * @param {Date} date The selected date
18872 * @event monthchange
18873 * Fires when the displayed month changes
18874 * @param {DatePicker} this
18875 * @param {Date} date The selected month
18877 'monthchange': true,
18879 * @event evententer
18880 * Fires when mouse over an event
18881 * @param {Calendar} this
18882 * @param {event} Event
18884 'evententer': true,
18886 * @event eventleave
18887 * Fires when the mouse leaves an
18888 * @param {Calendar} this
18891 'eventleave': true,
18893 * @event eventclick
18894 * Fires when the mouse click an
18895 * @param {Calendar} this
18904 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18907 * @cfg {Number} startDay
18908 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18916 getAutoCreate : function(){
18919 var fc_button = function(name, corner, style, content ) {
18920 return Roo.apply({},{
18922 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18924 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18927 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18938 style : 'width:100%',
18945 cls : 'fc-header-left',
18947 fc_button('prev', 'left', 'arrow', '‹' ),
18948 fc_button('next', 'right', 'arrow', '›' ),
18949 { tag: 'span', cls: 'fc-header-space' },
18950 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18958 cls : 'fc-header-center',
18962 cls: 'fc-header-title',
18965 html : 'month / year'
18973 cls : 'fc-header-right',
18975 /* fc_button('month', 'left', '', 'month' ),
18976 fc_button('week', '', '', 'week' ),
18977 fc_button('day', 'right', '', 'day' )
18989 header = this.header;
18992 var cal_heads = function() {
18994 // fixme - handle this.
18996 for (var i =0; i < Date.dayNames.length; i++) {
18997 var d = Date.dayNames[i];
19000 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19001 html : d.substring(0,3)
19005 ret[0].cls += ' fc-first';
19006 ret[6].cls += ' fc-last';
19009 var cal_cell = function(n) {
19012 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19017 cls: 'fc-day-number',
19021 cls: 'fc-day-content',
19025 style: 'position: relative;' // height: 17px;
19037 var cal_rows = function() {
19040 for (var r = 0; r < 6; r++) {
19047 for (var i =0; i < Date.dayNames.length; i++) {
19048 var d = Date.dayNames[i];
19049 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19052 row.cn[0].cls+=' fc-first';
19053 row.cn[0].cn[0].style = 'min-height:90px';
19054 row.cn[6].cls+=' fc-last';
19058 ret[0].cls += ' fc-first';
19059 ret[4].cls += ' fc-prev-last';
19060 ret[5].cls += ' fc-last';
19067 cls: 'fc-border-separate',
19068 style : 'width:100%',
19076 cls : 'fc-first fc-last',
19094 cls : 'fc-content',
19095 style : "position: relative;",
19098 cls : 'fc-view fc-view-month fc-grid',
19099 style : 'position: relative',
19100 unselectable : 'on',
19103 cls : 'fc-event-container',
19104 style : 'position:absolute;z-index:8;top:0;left:0;'
19122 initEvents : function()
19125 throw "can not find store for calendar";
19131 style: "text-align:center",
19135 style: "background-color:white;width:50%;margin:250 auto",
19139 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19150 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19152 var size = this.el.select('.fc-content', true).first().getSize();
19153 this.maskEl.setSize(size.width, size.height);
19154 this.maskEl.enableDisplayMode("block");
19155 if(!this.loadMask){
19156 this.maskEl.hide();
19159 this.store = Roo.factory(this.store, Roo.data);
19160 this.store.on('load', this.onLoad, this);
19161 this.store.on('beforeload', this.onBeforeLoad, this);
19165 this.cells = this.el.select('.fc-day',true);
19166 //Roo.log(this.cells);
19167 this.textNodes = this.el.query('.fc-day-number');
19168 this.cells.addClassOnOver('fc-state-hover');
19170 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19171 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19172 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19173 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19175 this.on('monthchange', this.onMonthChange, this);
19177 this.update(new Date().clearTime());
19180 resize : function() {
19181 var sz = this.el.getSize();
19183 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19184 this.el.select('.fc-day-content div',true).setHeight(34);
19189 showPrevMonth : function(e){
19190 this.update(this.activeDate.add("mo", -1));
19192 showToday : function(e){
19193 this.update(new Date().clearTime());
19196 showNextMonth : function(e){
19197 this.update(this.activeDate.add("mo", 1));
19201 showPrevYear : function(){
19202 this.update(this.activeDate.add("y", -1));
19206 showNextYear : function(){
19207 this.update(this.activeDate.add("y", 1));
19212 update : function(date)
19214 var vd = this.activeDate;
19215 this.activeDate = date;
19216 // if(vd && this.el){
19217 // var t = date.getTime();
19218 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19219 // Roo.log('using add remove');
19221 // this.fireEvent('monthchange', this, date);
19223 // this.cells.removeClass("fc-state-highlight");
19224 // this.cells.each(function(c){
19225 // if(c.dateValue == t){
19226 // c.addClass("fc-state-highlight");
19227 // setTimeout(function(){
19228 // try{c.dom.firstChild.focus();}catch(e){}
19238 var days = date.getDaysInMonth();
19240 var firstOfMonth = date.getFirstDateOfMonth();
19241 var startingPos = firstOfMonth.getDay()-this.startDay;
19243 if(startingPos < this.startDay){
19247 var pm = date.add(Date.MONTH, -1);
19248 var prevStart = pm.getDaysInMonth()-startingPos;
19250 this.cells = this.el.select('.fc-day',true);
19251 this.textNodes = this.el.query('.fc-day-number');
19252 this.cells.addClassOnOver('fc-state-hover');
19254 var cells = this.cells.elements;
19255 var textEls = this.textNodes;
19257 Roo.each(cells, function(cell){
19258 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19261 days += startingPos;
19263 // convert everything to numbers so it's fast
19264 var day = 86400000;
19265 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19268 //Roo.log(prevStart);
19270 var today = new Date().clearTime().getTime();
19271 var sel = date.clearTime().getTime();
19272 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19273 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19274 var ddMatch = this.disabledDatesRE;
19275 var ddText = this.disabledDatesText;
19276 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19277 var ddaysText = this.disabledDaysText;
19278 var format = this.format;
19280 var setCellClass = function(cal, cell){
19284 //Roo.log('set Cell Class');
19286 var t = d.getTime();
19290 cell.dateValue = t;
19292 cell.className += " fc-today";
19293 cell.className += " fc-state-highlight";
19294 cell.title = cal.todayText;
19297 // disable highlight in other month..
19298 //cell.className += " fc-state-highlight";
19303 cell.className = " fc-state-disabled";
19304 cell.title = cal.minText;
19308 cell.className = " fc-state-disabled";
19309 cell.title = cal.maxText;
19313 if(ddays.indexOf(d.getDay()) != -1){
19314 cell.title = ddaysText;
19315 cell.className = " fc-state-disabled";
19318 if(ddMatch && format){
19319 var fvalue = d.dateFormat(format);
19320 if(ddMatch.test(fvalue)){
19321 cell.title = ddText.replace("%0", fvalue);
19322 cell.className = " fc-state-disabled";
19326 if (!cell.initialClassName) {
19327 cell.initialClassName = cell.dom.className;
19330 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19335 for(; i < startingPos; i++) {
19336 textEls[i].innerHTML = (++prevStart);
19337 d.setDate(d.getDate()+1);
19339 cells[i].className = "fc-past fc-other-month";
19340 setCellClass(this, cells[i]);
19345 for(; i < days; i++){
19346 intDay = i - startingPos + 1;
19347 textEls[i].innerHTML = (intDay);
19348 d.setDate(d.getDate()+1);
19350 cells[i].className = ''; // "x-date-active";
19351 setCellClass(this, cells[i]);
19355 for(; i < 42; i++) {
19356 textEls[i].innerHTML = (++extraDays);
19357 d.setDate(d.getDate()+1);
19359 cells[i].className = "fc-future fc-other-month";
19360 setCellClass(this, cells[i]);
19363 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19365 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19367 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19368 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19370 if(totalRows != 6){
19371 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19372 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19375 this.fireEvent('monthchange', this, date);
19379 if(!this.internalRender){
19380 var main = this.el.dom.firstChild;
19381 var w = main.offsetWidth;
19382 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19383 Roo.fly(main).setWidth(w);
19384 this.internalRender = true;
19385 // opera does not respect the auto grow header center column
19386 // then, after it gets a width opera refuses to recalculate
19387 // without a second pass
19388 if(Roo.isOpera && !this.secondPass){
19389 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19390 this.secondPass = true;
19391 this.update.defer(10, this, [date]);
19398 findCell : function(dt) {
19399 dt = dt.clearTime().getTime();
19401 this.cells.each(function(c){
19402 //Roo.log("check " +c.dateValue + '?=' + dt);
19403 if(c.dateValue == dt){
19413 findCells : function(ev) {
19414 var s = ev.start.clone().clearTime().getTime();
19416 var e= ev.end.clone().clearTime().getTime();
19419 this.cells.each(function(c){
19420 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19422 if(c.dateValue > e){
19425 if(c.dateValue < s){
19434 // findBestRow: function(cells)
19438 // for (var i =0 ; i < cells.length;i++) {
19439 // ret = Math.max(cells[i].rows || 0,ret);
19446 addItem : function(ev)
19448 // look for vertical location slot in
19449 var cells = this.findCells(ev);
19451 // ev.row = this.findBestRow(cells);
19453 // work out the location.
19457 for(var i =0; i < cells.length; i++) {
19459 cells[i].row = cells[0].row;
19462 cells[i].row = cells[i].row + 1;
19472 if (crow.start.getY() == cells[i].getY()) {
19474 crow.end = cells[i];
19491 cells[0].events.push(ev);
19493 this.calevents.push(ev);
19496 clearEvents: function() {
19498 if(!this.calevents){
19502 Roo.each(this.cells.elements, function(c){
19508 Roo.each(this.calevents, function(e) {
19509 Roo.each(e.els, function(el) {
19510 el.un('mouseenter' ,this.onEventEnter, this);
19511 el.un('mouseleave' ,this.onEventLeave, this);
19516 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19522 renderEvents: function()
19526 this.cells.each(function(c) {
19535 if(c.row != c.events.length){
19536 r = 4 - (4 - (c.row - c.events.length));
19539 c.events = ev.slice(0, r);
19540 c.more = ev.slice(r);
19542 if(c.more.length && c.more.length == 1){
19543 c.events.push(c.more.pop());
19546 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19550 this.cells.each(function(c) {
19552 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19555 for (var e = 0; e < c.events.length; e++){
19556 var ev = c.events[e];
19557 var rows = ev.rows;
19559 for(var i = 0; i < rows.length; i++) {
19561 // how many rows should it span..
19564 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19565 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19567 unselectable : "on",
19570 cls: 'fc-event-inner',
19574 // cls: 'fc-event-time',
19575 // html : cells.length > 1 ? '' : ev.time
19579 cls: 'fc-event-title',
19580 html : String.format('{0}', ev.title)
19587 cls: 'ui-resizable-handle ui-resizable-e',
19588 html : '  '
19595 cfg.cls += ' fc-event-start';
19597 if ((i+1) == rows.length) {
19598 cfg.cls += ' fc-event-end';
19601 var ctr = _this.el.select('.fc-event-container',true).first();
19602 var cg = ctr.createChild(cfg);
19604 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19605 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19607 var r = (c.more.length) ? 1 : 0;
19608 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19609 cg.setWidth(ebox.right - sbox.x -2);
19611 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19612 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19613 cg.on('click', _this.onEventClick, _this, ev);
19624 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19625 style : 'position: absolute',
19626 unselectable : "on",
19629 cls: 'fc-event-inner',
19633 cls: 'fc-event-title',
19641 cls: 'ui-resizable-handle ui-resizable-e',
19642 html : '  '
19648 var ctr = _this.el.select('.fc-event-container',true).first();
19649 var cg = ctr.createChild(cfg);
19651 var sbox = c.select('.fc-day-content',true).first().getBox();
19652 var ebox = c.select('.fc-day-content',true).first().getBox();
19654 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19655 cg.setWidth(ebox.right - sbox.x -2);
19657 cg.on('click', _this.onMoreEventClick, _this, c.more);
19667 onEventEnter: function (e, el,event,d) {
19668 this.fireEvent('evententer', this, el, event);
19671 onEventLeave: function (e, el,event,d) {
19672 this.fireEvent('eventleave', this, el, event);
19675 onEventClick: function (e, el,event,d) {
19676 this.fireEvent('eventclick', this, el, event);
19679 onMonthChange: function () {
19683 onMoreEventClick: function(e, el, more)
19687 this.calpopover.placement = 'right';
19688 this.calpopover.setTitle('More');
19690 this.calpopover.setContent('');
19692 var ctr = this.calpopover.el.select('.popover-content', true).first();
19694 Roo.each(more, function(m){
19696 cls : 'fc-event-hori fc-event-draggable',
19699 var cg = ctr.createChild(cfg);
19701 cg.on('click', _this.onEventClick, _this, m);
19704 this.calpopover.show(el);
19709 onLoad: function ()
19711 this.calevents = [];
19714 if(this.store.getCount() > 0){
19715 this.store.data.each(function(d){
19718 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19719 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19720 time : d.data.start_time,
19721 title : d.data.title,
19722 description : d.data.description,
19723 venue : d.data.venue
19728 this.renderEvents();
19730 if(this.calevents.length && this.loadMask){
19731 this.maskEl.hide();
19735 onBeforeLoad: function()
19737 this.clearEvents();
19739 this.maskEl.show();
19753 * @class Roo.bootstrap.Popover
19754 * @extends Roo.bootstrap.Component
19755 * Bootstrap Popover class
19756 * @cfg {String} html contents of the popover (or false to use children..)
19757 * @cfg {String} title of popover (or false to hide)
19758 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19759 * @cfg {String} trigger click || hover (or false to trigger manually)
19760 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19761 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19762 * - if false and it has a 'parent' then it will be automatically added to that element
19763 * - if string - Roo.get will be called
19764 * @cfg {Number} delay - delay before showing
19767 * Create a new Popover
19768 * @param {Object} config The config object
19771 Roo.bootstrap.Popover = function(config){
19772 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19778 * After the popover show
19780 * @param {Roo.bootstrap.Popover} this
19785 * After the popover hide
19787 * @param {Roo.bootstrap.Popover} this
19793 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19798 placement : 'right',
19799 trigger : 'hover', // hover
19805 can_build_overlaid : false,
19807 maskEl : false, // the mask element
19810 alignEl : false, // when show is called with an element - this get's stored.
19812 getChildContainer : function()
19814 return this.contentEl;
19817 getPopoverHeader : function()
19819 this.title = true; // flag not to hide it..
19820 this.headerEl.addClass('p-0');
19821 return this.headerEl
19825 getAutoCreate : function(){
19828 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19829 style: 'display:block',
19835 cls : 'popover-inner ',
19839 cls: 'popover-title popover-header',
19840 html : this.title === false ? '' : this.title
19843 cls : 'popover-content popover-body ' + (this.cls || ''),
19844 html : this.html || ''
19855 * @param {string} the title
19857 setTitle: function(str)
19861 this.headerEl.dom.innerHTML = str;
19866 * @param {string} the body content
19868 setContent: function(str)
19871 if (this.contentEl) {
19872 this.contentEl.dom.innerHTML = str;
19876 // as it get's added to the bottom of the page.
19877 onRender : function(ct, position)
19879 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19884 var cfg = Roo.apply({}, this.getAutoCreate());
19888 cfg.cls += ' ' + this.cls;
19891 cfg.style = this.style;
19893 //Roo.log("adding to ");
19894 this.el = Roo.get(document.body).createChild(cfg, position);
19895 // Roo.log(this.el);
19898 this.contentEl = this.el.select('.popover-content',true).first();
19899 this.headerEl = this.el.select('.popover-title',true).first();
19902 if(typeof(this.items) != 'undefined'){
19903 var items = this.items;
19906 for(var i =0;i < items.length;i++) {
19907 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19911 this.items = nitems;
19913 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19914 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19921 resizeMask : function()
19923 this.maskEl.setSize(
19924 Roo.lib.Dom.getViewWidth(true),
19925 Roo.lib.Dom.getViewHeight(true)
19929 initEvents : function()
19933 Roo.bootstrap.Popover.register(this);
19936 this.arrowEl = this.el.select('.arrow',true).first();
19937 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19938 this.el.enableDisplayMode('block');
19942 if (this.over === false && !this.parent()) {
19945 if (this.triggers === false) {
19950 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19951 var triggers = this.trigger ? this.trigger.split(' ') : [];
19952 Roo.each(triggers, function(trigger) {
19954 if (trigger == 'click') {
19955 on_el.on('click', this.toggle, this);
19956 } else if (trigger != 'manual') {
19957 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19958 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19960 on_el.on(eventIn ,this.enter, this);
19961 on_el.on(eventOut, this.leave, this);
19971 toggle : function () {
19972 this.hoverState == 'in' ? this.leave() : this.enter();
19975 enter : function () {
19977 clearTimeout(this.timeout);
19979 this.hoverState = 'in';
19981 if (!this.delay || !this.delay.show) {
19986 this.timeout = setTimeout(function () {
19987 if (_t.hoverState == 'in') {
19990 }, this.delay.show)
19993 leave : function() {
19994 clearTimeout(this.timeout);
19996 this.hoverState = 'out';
19998 if (!this.delay || !this.delay.hide) {
20003 this.timeout = setTimeout(function () {
20004 if (_t.hoverState == 'out') {
20007 }, this.delay.hide)
20011 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20012 * @param {string} (left|right|top|bottom) position
20014 show : function (on_el, placement)
20016 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20017 on_el = on_el || false; // default to false
20020 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20021 on_el = this.parent().el;
20022 } else if (this.over) {
20023 Roo.get(this.over);
20028 this.alignEl = Roo.get( on_el );
20031 this.render(document.body);
20037 if (this.title === false) {
20038 this.headerEl.hide();
20043 this.el.dom.style.display = 'block';
20046 if (this.alignEl) {
20047 this.updatePosition(this.placement, true);
20050 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20051 var es = this.el.getSize();
20052 var x = Roo.lib.Dom.getViewWidth()/2;
20053 var y = Roo.lib.Dom.getViewHeight()/2;
20054 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20059 //var arrow = this.el.select('.arrow',true).first();
20060 //arrow.set(align[2],
20062 this.el.addClass('in');
20066 this.hoverState = 'in';
20069 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20070 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20071 this.maskEl.dom.style.display = 'block';
20072 this.maskEl.addClass('show');
20074 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20076 this.fireEvent('show', this);
20080 * fire this manually after loading a grid in the table for example
20081 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20082 * @param {Boolean} try and move it if we cant get right position.
20084 updatePosition : function(placement, try_move)
20086 // allow for calling with no parameters
20087 placement = placement ? placement : this.placement;
20088 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20090 this.el.removeClass([
20091 'fade','top','bottom', 'left', 'right','in',
20092 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20094 this.el.addClass(placement + ' bs-popover-' + placement);
20096 if (!this.alignEl ) {
20100 switch (placement) {
20102 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20103 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20104 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20105 //normal display... or moved up/down.
20106 this.el.setXY(offset);
20107 var xy = this.alignEl.getAnchorXY('tr', false);
20109 this.arrowEl.setXY(xy);
20112 // continue through...
20113 return this.updatePosition('left', false);
20117 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20118 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20119 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20120 //normal display... or moved up/down.
20121 this.el.setXY(offset);
20122 var xy = this.alignEl.getAnchorXY('tl', false);
20123 xy[0]-=10;xy[1]+=5; // << fix me
20124 this.arrowEl.setXY(xy);
20128 return this.updatePosition('right', false);
20131 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20132 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20133 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20134 //normal display... or moved up/down.
20135 this.el.setXY(offset);
20136 var xy = this.alignEl.getAnchorXY('t', false);
20137 xy[1]-=10; // << fix me
20138 this.arrowEl.setXY(xy);
20142 return this.updatePosition('bottom', false);
20145 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20146 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20147 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20148 //normal display... or moved up/down.
20149 this.el.setXY(offset);
20150 var xy = this.alignEl.getAnchorXY('b', false);
20151 xy[1]+=2; // << fix me
20152 this.arrowEl.setXY(xy);
20156 return this.updatePosition('top', false);
20167 this.el.setXY([0,0]);
20168 this.el.removeClass('in');
20170 this.hoverState = null;
20171 this.maskEl.hide(); // always..
20172 this.fireEvent('hide', this);
20178 Roo.apply(Roo.bootstrap.Popover, {
20181 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20182 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20183 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20184 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20189 clickHander : false,
20192 onMouseDown : function(e)
20194 if (!e.getTarget(".roo-popover")) {
20202 register : function(popup)
20204 if (!Roo.bootstrap.Popover.clickHandler) {
20205 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20207 // hide other popups.
20209 this.popups.push(popup);
20211 hideAll : function()
20213 this.popups.forEach(function(p) {
20221 * Card header - holder for the card header elements.
20226 * @class Roo.bootstrap.PopoverNav
20227 * @extends Roo.bootstrap.NavGroup
20228 * Bootstrap Popover header navigation class
20230 * Create a new Popover Header Navigation
20231 * @param {Object} config The config object
20234 Roo.bootstrap.PopoverNav = function(config){
20235 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20238 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20241 container_method : 'getPopoverHeader'
20259 * @class Roo.bootstrap.Progress
20260 * @extends Roo.bootstrap.Component
20261 * Bootstrap Progress class
20262 * @cfg {Boolean} striped striped of the progress bar
20263 * @cfg {Boolean} active animated of the progress bar
20267 * Create a new Progress
20268 * @param {Object} config The config object
20271 Roo.bootstrap.Progress = function(config){
20272 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20275 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20280 getAutoCreate : function(){
20288 cfg.cls += ' progress-striped';
20292 cfg.cls += ' active';
20311 * @class Roo.bootstrap.ProgressBar
20312 * @extends Roo.bootstrap.Component
20313 * Bootstrap ProgressBar class
20314 * @cfg {Number} aria_valuenow aria-value now
20315 * @cfg {Number} aria_valuemin aria-value min
20316 * @cfg {Number} aria_valuemax aria-value max
20317 * @cfg {String} label label for the progress bar
20318 * @cfg {String} panel (success | info | warning | danger )
20319 * @cfg {String} role role of the progress bar
20320 * @cfg {String} sr_only text
20324 * Create a new ProgressBar
20325 * @param {Object} config The config object
20328 Roo.bootstrap.ProgressBar = function(config){
20329 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20332 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20336 aria_valuemax : 100,
20342 getAutoCreate : function()
20347 cls: 'progress-bar',
20348 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20360 cfg.role = this.role;
20363 if(this.aria_valuenow){
20364 cfg['aria-valuenow'] = this.aria_valuenow;
20367 if(this.aria_valuemin){
20368 cfg['aria-valuemin'] = this.aria_valuemin;
20371 if(this.aria_valuemax){
20372 cfg['aria-valuemax'] = this.aria_valuemax;
20375 if(this.label && !this.sr_only){
20376 cfg.html = this.label;
20380 cfg.cls += ' progress-bar-' + this.panel;
20386 update : function(aria_valuenow)
20388 this.aria_valuenow = aria_valuenow;
20390 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20405 * @class Roo.bootstrap.TabGroup
20406 * @extends Roo.bootstrap.Column
20407 * Bootstrap Column class
20408 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20409 * @cfg {Boolean} carousel true to make the group behave like a carousel
20410 * @cfg {Boolean} bullets show bullets for the panels
20411 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20412 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20413 * @cfg {Boolean} showarrow (true|false) show arrow default true
20416 * Create a new TabGroup
20417 * @param {Object} config The config object
20420 Roo.bootstrap.TabGroup = function(config){
20421 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20423 this.navId = Roo.id();
20426 Roo.bootstrap.TabGroup.register(this);
20430 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20433 transition : false,
20438 slideOnTouch : false,
20441 getAutoCreate : function()
20443 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20445 cfg.cls += ' tab-content';
20447 if (this.carousel) {
20448 cfg.cls += ' carousel slide';
20451 cls : 'carousel-inner',
20455 if(this.bullets && !Roo.isTouch){
20458 cls : 'carousel-bullets',
20462 if(this.bullets_cls){
20463 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20470 cfg.cn[0].cn.push(bullets);
20473 if(this.showarrow){
20474 cfg.cn[0].cn.push({
20476 class : 'carousel-arrow',
20480 class : 'carousel-prev',
20484 class : 'fa fa-chevron-left'
20490 class : 'carousel-next',
20494 class : 'fa fa-chevron-right'
20507 initEvents: function()
20509 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20510 // this.el.on("touchstart", this.onTouchStart, this);
20513 if(this.autoslide){
20516 this.slideFn = window.setInterval(function() {
20517 _this.showPanelNext();
20521 if(this.showarrow){
20522 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20523 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20529 // onTouchStart : function(e, el, o)
20531 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20535 // this.showPanelNext();
20539 getChildContainer : function()
20541 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20545 * register a Navigation item
20546 * @param {Roo.bootstrap.NavItem} the navitem to add
20548 register : function(item)
20550 this.tabs.push( item);
20551 item.navId = this.navId; // not really needed..
20556 getActivePanel : function()
20559 Roo.each(this.tabs, function(t) {
20569 getPanelByName : function(n)
20572 Roo.each(this.tabs, function(t) {
20573 if (t.tabId == n) {
20581 indexOfPanel : function(p)
20584 Roo.each(this.tabs, function(t,i) {
20585 if (t.tabId == p.tabId) {
20594 * show a specific panel
20595 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20596 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20598 showPanel : function (pan)
20600 if(this.transition || typeof(pan) == 'undefined'){
20601 Roo.log("waiting for the transitionend");
20605 if (typeof(pan) == 'number') {
20606 pan = this.tabs[pan];
20609 if (typeof(pan) == 'string') {
20610 pan = this.getPanelByName(pan);
20613 var cur = this.getActivePanel();
20616 Roo.log('pan or acitve pan is undefined');
20620 if (pan.tabId == this.getActivePanel().tabId) {
20624 if (false === cur.fireEvent('beforedeactivate')) {
20628 if(this.bullets > 0 && !Roo.isTouch){
20629 this.setActiveBullet(this.indexOfPanel(pan));
20632 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20634 //class="carousel-item carousel-item-next carousel-item-left"
20636 this.transition = true;
20637 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20638 var lr = dir == 'next' ? 'left' : 'right';
20639 pan.el.addClass(dir); // or prev
20640 pan.el.addClass('carousel-item-' + dir); // or prev
20641 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20642 cur.el.addClass(lr); // or right
20643 pan.el.addClass(lr);
20644 cur.el.addClass('carousel-item-' +lr); // or right
20645 pan.el.addClass('carousel-item-' +lr);
20649 cur.el.on('transitionend', function() {
20650 Roo.log("trans end?");
20652 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20653 pan.setActive(true);
20655 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20656 cur.setActive(false);
20658 _this.transition = false;
20660 }, this, { single: true } );
20665 cur.setActive(false);
20666 pan.setActive(true);
20671 showPanelNext : function()
20673 var i = this.indexOfPanel(this.getActivePanel());
20675 if (i >= this.tabs.length - 1 && !this.autoslide) {
20679 if (i >= this.tabs.length - 1 && this.autoslide) {
20683 this.showPanel(this.tabs[i+1]);
20686 showPanelPrev : function()
20688 var i = this.indexOfPanel(this.getActivePanel());
20690 if (i < 1 && !this.autoslide) {
20694 if (i < 1 && this.autoslide) {
20695 i = this.tabs.length;
20698 this.showPanel(this.tabs[i-1]);
20702 addBullet: function()
20704 if(!this.bullets || Roo.isTouch){
20707 var ctr = this.el.select('.carousel-bullets',true).first();
20708 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20709 var bullet = ctr.createChild({
20710 cls : 'bullet bullet-' + i
20711 },ctr.dom.lastChild);
20716 bullet.on('click', (function(e, el, o, ii, t){
20718 e.preventDefault();
20720 this.showPanel(ii);
20722 if(this.autoslide && this.slideFn){
20723 clearInterval(this.slideFn);
20724 this.slideFn = window.setInterval(function() {
20725 _this.showPanelNext();
20729 }).createDelegate(this, [i, bullet], true));
20734 setActiveBullet : function(i)
20740 Roo.each(this.el.select('.bullet', true).elements, function(el){
20741 el.removeClass('selected');
20744 var bullet = this.el.select('.bullet-' + i, true).first();
20750 bullet.addClass('selected');
20761 Roo.apply(Roo.bootstrap.TabGroup, {
20765 * register a Navigation Group
20766 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20768 register : function(navgrp)
20770 this.groups[navgrp.navId] = navgrp;
20774 * fetch a Navigation Group based on the navigation ID
20775 * if one does not exist , it will get created.
20776 * @param {string} the navgroup to add
20777 * @returns {Roo.bootstrap.NavGroup} the navgroup
20779 get: function(navId) {
20780 if (typeof(this.groups[navId]) == 'undefined') {
20781 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20783 return this.groups[navId] ;
20798 * @class Roo.bootstrap.TabPanel
20799 * @extends Roo.bootstrap.Component
20800 * Bootstrap TabPanel class
20801 * @cfg {Boolean} active panel active
20802 * @cfg {String} html panel content
20803 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20804 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20805 * @cfg {String} href click to link..
20806 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20810 * Create a new TabPanel
20811 * @param {Object} config The config object
20814 Roo.bootstrap.TabPanel = function(config){
20815 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20819 * Fires when the active status changes
20820 * @param {Roo.bootstrap.TabPanel} this
20821 * @param {Boolean} state the new state
20826 * @event beforedeactivate
20827 * Fires before a tab is de-activated - can be used to do validation on a form.
20828 * @param {Roo.bootstrap.TabPanel} this
20829 * @return {Boolean} false if there is an error
20832 'beforedeactivate': true
20835 this.tabId = this.tabId || Roo.id();
20839 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20846 touchSlide : false,
20847 getAutoCreate : function(){
20852 // item is needed for carousel - not sure if it has any effect otherwise
20853 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20854 html: this.html || ''
20858 cfg.cls += ' active';
20862 cfg.tabId = this.tabId;
20870 initEvents: function()
20872 var p = this.parent();
20874 this.navId = this.navId || p.navId;
20876 if (typeof(this.navId) != 'undefined') {
20877 // not really needed.. but just in case.. parent should be a NavGroup.
20878 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20882 var i = tg.tabs.length - 1;
20884 if(this.active && tg.bullets > 0 && i < tg.bullets){
20885 tg.setActiveBullet(i);
20889 this.el.on('click', this.onClick, this);
20891 if(Roo.isTouch && this.touchSlide){
20892 this.el.on("touchstart", this.onTouchStart, this);
20893 this.el.on("touchmove", this.onTouchMove, this);
20894 this.el.on("touchend", this.onTouchEnd, this);
20899 onRender : function(ct, position)
20901 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20904 setActive : function(state)
20906 Roo.log("panel - set active " + this.tabId + "=" + state);
20908 this.active = state;
20910 this.el.removeClass('active');
20912 } else if (!this.el.hasClass('active')) {
20913 this.el.addClass('active');
20916 this.fireEvent('changed', this, state);
20919 onClick : function(e)
20921 e.preventDefault();
20923 if(!this.href.length){
20927 window.location.href = this.href;
20936 onTouchStart : function(e)
20938 this.swiping = false;
20940 this.startX = e.browserEvent.touches[0].clientX;
20941 this.startY = e.browserEvent.touches[0].clientY;
20944 onTouchMove : function(e)
20946 this.swiping = true;
20948 this.endX = e.browserEvent.touches[0].clientX;
20949 this.endY = e.browserEvent.touches[0].clientY;
20952 onTouchEnd : function(e)
20959 var tabGroup = this.parent();
20961 if(this.endX > this.startX){ // swiping right
20962 tabGroup.showPanelPrev();
20966 if(this.startX > this.endX){ // swiping left
20967 tabGroup.showPanelNext();
20986 * @class Roo.bootstrap.DateField
20987 * @extends Roo.bootstrap.Input
20988 * Bootstrap DateField class
20989 * @cfg {Number} weekStart default 0
20990 * @cfg {String} viewMode default empty, (months|years)
20991 * @cfg {String} minViewMode default empty, (months|years)
20992 * @cfg {Number} startDate default -Infinity
20993 * @cfg {Number} endDate default Infinity
20994 * @cfg {Boolean} todayHighlight default false
20995 * @cfg {Boolean} todayBtn default false
20996 * @cfg {Boolean} calendarWeeks default false
20997 * @cfg {Object} daysOfWeekDisabled default empty
20998 * @cfg {Boolean} singleMode default false (true | false)
21000 * @cfg {Boolean} keyboardNavigation default true
21001 * @cfg {String} language default en
21004 * Create a new DateField
21005 * @param {Object} config The config object
21008 Roo.bootstrap.DateField = function(config){
21009 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21013 * Fires when this field show.
21014 * @param {Roo.bootstrap.DateField} this
21015 * @param {Mixed} date The date value
21020 * Fires when this field hide.
21021 * @param {Roo.bootstrap.DateField} this
21022 * @param {Mixed} date The date value
21027 * Fires when select a date.
21028 * @param {Roo.bootstrap.DateField} this
21029 * @param {Mixed} date The date value
21033 * @event beforeselect
21034 * Fires when before select a date.
21035 * @param {Roo.bootstrap.DateField} this
21036 * @param {Mixed} date The date value
21038 beforeselect : true
21042 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21045 * @cfg {String} format
21046 * The default date format string which can be overriden for localization support. The format must be
21047 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21051 * @cfg {String} altFormats
21052 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21053 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21055 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21063 todayHighlight : false,
21069 keyboardNavigation: true,
21071 calendarWeeks: false,
21073 startDate: -Infinity,
21077 daysOfWeekDisabled: [],
21081 singleMode : false,
21083 UTCDate: function()
21085 return new Date(Date.UTC.apply(Date, arguments));
21088 UTCToday: function()
21090 var today = new Date();
21091 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21094 getDate: function() {
21095 var d = this.getUTCDate();
21096 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21099 getUTCDate: function() {
21103 setDate: function(d) {
21104 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21107 setUTCDate: function(d) {
21109 this.setValue(this.formatDate(this.date));
21112 onRender: function(ct, position)
21115 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21117 this.language = this.language || 'en';
21118 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21119 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21121 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21122 this.format = this.format || 'm/d/y';
21123 this.isInline = false;
21124 this.isInput = true;
21125 this.component = this.el.select('.add-on', true).first() || false;
21126 this.component = (this.component && this.component.length === 0) ? false : this.component;
21127 this.hasInput = this.component && this.inputEl().length;
21129 if (typeof(this.minViewMode === 'string')) {
21130 switch (this.minViewMode) {
21132 this.minViewMode = 1;
21135 this.minViewMode = 2;
21138 this.minViewMode = 0;
21143 if (typeof(this.viewMode === 'string')) {
21144 switch (this.viewMode) {
21157 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21159 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21161 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21163 this.picker().on('mousedown', this.onMousedown, this);
21164 this.picker().on('click', this.onClick, this);
21166 this.picker().addClass('datepicker-dropdown');
21168 this.startViewMode = this.viewMode;
21170 if(this.singleMode){
21171 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21172 v.setVisibilityMode(Roo.Element.DISPLAY);
21176 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21177 v.setStyle('width', '189px');
21181 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21182 if(!this.calendarWeeks){
21187 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21188 v.attr('colspan', function(i, val){
21189 return parseInt(val) + 1;
21194 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21196 this.setStartDate(this.startDate);
21197 this.setEndDate(this.endDate);
21199 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21206 if(this.isInline) {
21211 picker : function()
21213 return this.pickerEl;
21214 // return this.el.select('.datepicker', true).first();
21217 fillDow: function()
21219 var dowCnt = this.weekStart;
21228 if(this.calendarWeeks){
21236 while (dowCnt < this.weekStart + 7) {
21240 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21244 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21247 fillMonths: function()
21250 var months = this.picker().select('>.datepicker-months td', true).first();
21252 months.dom.innerHTML = '';
21258 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21261 months.createChild(month);
21268 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;
21270 if (this.date < this.startDate) {
21271 this.viewDate = new Date(this.startDate);
21272 } else if (this.date > this.endDate) {
21273 this.viewDate = new Date(this.endDate);
21275 this.viewDate = new Date(this.date);
21283 var d = new Date(this.viewDate),
21284 year = d.getUTCFullYear(),
21285 month = d.getUTCMonth(),
21286 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21287 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21288 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21289 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21290 currentDate = this.date && this.date.valueOf(),
21291 today = this.UTCToday();
21293 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21295 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21297 // this.picker.select('>tfoot th.today').
21298 // .text(dates[this.language].today)
21299 // .toggle(this.todayBtn !== false);
21301 this.updateNavArrows();
21304 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21306 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21308 prevMonth.setUTCDate(day);
21310 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21312 var nextMonth = new Date(prevMonth);
21314 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21316 nextMonth = nextMonth.valueOf();
21318 var fillMonths = false;
21320 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21322 while(prevMonth.valueOf() <= nextMonth) {
21325 if (prevMonth.getUTCDay() === this.weekStart) {
21327 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21335 if(this.calendarWeeks){
21336 // ISO 8601: First week contains first thursday.
21337 // ISO also states week starts on Monday, but we can be more abstract here.
21339 // Start of current week: based on weekstart/current date
21340 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21341 // Thursday of this week
21342 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21343 // First Thursday of year, year from thursday
21344 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21345 // Calendar week: ms between thursdays, div ms per day, div 7 days
21346 calWeek = (th - yth) / 864e5 / 7 + 1;
21348 fillMonths.cn.push({
21356 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21358 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21361 if (this.todayHighlight &&
21362 prevMonth.getUTCFullYear() == today.getFullYear() &&
21363 prevMonth.getUTCMonth() == today.getMonth() &&
21364 prevMonth.getUTCDate() == today.getDate()) {
21365 clsName += ' today';
21368 if (currentDate && prevMonth.valueOf() === currentDate) {
21369 clsName += ' active';
21372 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21373 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21374 clsName += ' disabled';
21377 fillMonths.cn.push({
21379 cls: 'day ' + clsName,
21380 html: prevMonth.getDate()
21383 prevMonth.setDate(prevMonth.getDate()+1);
21386 var currentYear = this.date && this.date.getUTCFullYear();
21387 var currentMonth = this.date && this.date.getUTCMonth();
21389 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21391 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21392 v.removeClass('active');
21394 if(currentYear === year && k === currentMonth){
21395 v.addClass('active');
21398 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21399 v.addClass('disabled');
21405 year = parseInt(year/10, 10) * 10;
21407 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21409 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21412 for (var i = -1; i < 11; i++) {
21413 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21415 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21423 showMode: function(dir)
21426 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21429 Roo.each(this.picker().select('>div',true).elements, function(v){
21430 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21433 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21438 if(this.isInline) {
21442 this.picker().removeClass(['bottom', 'top']);
21444 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21446 * place to the top of element!
21450 this.picker().addClass('top');
21451 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21456 this.picker().addClass('bottom');
21458 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21461 parseDate : function(value)
21463 if(!value || value instanceof Date){
21466 var v = Date.parseDate(value, this.format);
21467 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21468 v = Date.parseDate(value, 'Y-m-d');
21470 if(!v && this.altFormats){
21471 if(!this.altFormatsArray){
21472 this.altFormatsArray = this.altFormats.split("|");
21474 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21475 v = Date.parseDate(value, this.altFormatsArray[i]);
21481 formatDate : function(date, fmt)
21483 return (!date || !(date instanceof Date)) ?
21484 date : date.dateFormat(fmt || this.format);
21487 onFocus : function()
21489 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21493 onBlur : function()
21495 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21497 var d = this.inputEl().getValue();
21504 showPopup : function()
21506 this.picker().show();
21510 this.fireEvent('showpopup', this, this.date);
21513 hidePopup : function()
21515 if(this.isInline) {
21518 this.picker().hide();
21519 this.viewMode = this.startViewMode;
21522 this.fireEvent('hidepopup', this, this.date);
21526 onMousedown: function(e)
21528 e.stopPropagation();
21529 e.preventDefault();
21534 Roo.bootstrap.DateField.superclass.keyup.call(this);
21538 setValue: function(v)
21540 if(this.fireEvent('beforeselect', this, v) !== false){
21541 var d = new Date(this.parseDate(v) ).clearTime();
21543 if(isNaN(d.getTime())){
21544 this.date = this.viewDate = '';
21545 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21549 v = this.formatDate(d);
21551 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21553 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21557 this.fireEvent('select', this, this.date);
21561 getValue: function()
21563 return this.formatDate(this.date);
21566 fireKey: function(e)
21568 if (!this.picker().isVisible()){
21569 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21575 var dateChanged = false,
21577 newDate, newViewDate;
21582 e.preventDefault();
21586 if (!this.keyboardNavigation) {
21589 dir = e.keyCode == 37 ? -1 : 1;
21592 newDate = this.moveYear(this.date, dir);
21593 newViewDate = this.moveYear(this.viewDate, dir);
21594 } else if (e.shiftKey){
21595 newDate = this.moveMonth(this.date, dir);
21596 newViewDate = this.moveMonth(this.viewDate, dir);
21598 newDate = new Date(this.date);
21599 newDate.setUTCDate(this.date.getUTCDate() + dir);
21600 newViewDate = new Date(this.viewDate);
21601 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21603 if (this.dateWithinRange(newDate)){
21604 this.date = newDate;
21605 this.viewDate = newViewDate;
21606 this.setValue(this.formatDate(this.date));
21608 e.preventDefault();
21609 dateChanged = true;
21614 if (!this.keyboardNavigation) {
21617 dir = e.keyCode == 38 ? -1 : 1;
21619 newDate = this.moveYear(this.date, dir);
21620 newViewDate = this.moveYear(this.viewDate, dir);
21621 } else if (e.shiftKey){
21622 newDate = this.moveMonth(this.date, dir);
21623 newViewDate = this.moveMonth(this.viewDate, dir);
21625 newDate = new Date(this.date);
21626 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21627 newViewDate = new Date(this.viewDate);
21628 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21630 if (this.dateWithinRange(newDate)){
21631 this.date = newDate;
21632 this.viewDate = newViewDate;
21633 this.setValue(this.formatDate(this.date));
21635 e.preventDefault();
21636 dateChanged = true;
21640 this.setValue(this.formatDate(this.date));
21642 e.preventDefault();
21645 this.setValue(this.formatDate(this.date));
21659 onClick: function(e)
21661 e.stopPropagation();
21662 e.preventDefault();
21664 var target = e.getTarget();
21666 if(target.nodeName.toLowerCase() === 'i'){
21667 target = Roo.get(target).dom.parentNode;
21670 var nodeName = target.nodeName;
21671 var className = target.className;
21672 var html = target.innerHTML;
21673 //Roo.log(nodeName);
21675 switch(nodeName.toLowerCase()) {
21677 switch(className) {
21683 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21684 switch(this.viewMode){
21686 this.viewDate = this.moveMonth(this.viewDate, dir);
21690 this.viewDate = this.moveYear(this.viewDate, dir);
21696 var date = new Date();
21697 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21699 this.setValue(this.formatDate(this.date));
21706 if (className.indexOf('disabled') < 0) {
21707 this.viewDate.setUTCDate(1);
21708 if (className.indexOf('month') > -1) {
21709 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21711 var year = parseInt(html, 10) || 0;
21712 this.viewDate.setUTCFullYear(year);
21716 if(this.singleMode){
21717 this.setValue(this.formatDate(this.viewDate));
21728 //Roo.log(className);
21729 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21730 var day = parseInt(html, 10) || 1;
21731 var year = (this.viewDate || new Date()).getUTCFullYear(),
21732 month = (this.viewDate || new Date()).getUTCMonth();
21734 if (className.indexOf('old') > -1) {
21741 } else if (className.indexOf('new') > -1) {
21749 //Roo.log([year,month,day]);
21750 this.date = this.UTCDate(year, month, day,0,0,0,0);
21751 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21753 //Roo.log(this.formatDate(this.date));
21754 this.setValue(this.formatDate(this.date));
21761 setStartDate: function(startDate)
21763 this.startDate = startDate || -Infinity;
21764 if (this.startDate !== -Infinity) {
21765 this.startDate = this.parseDate(this.startDate);
21768 this.updateNavArrows();
21771 setEndDate: function(endDate)
21773 this.endDate = endDate || Infinity;
21774 if (this.endDate !== Infinity) {
21775 this.endDate = this.parseDate(this.endDate);
21778 this.updateNavArrows();
21781 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21783 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21784 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21785 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21787 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21788 return parseInt(d, 10);
21791 this.updateNavArrows();
21794 updateNavArrows: function()
21796 if(this.singleMode){
21800 var d = new Date(this.viewDate),
21801 year = d.getUTCFullYear(),
21802 month = d.getUTCMonth();
21804 Roo.each(this.picker().select('.prev', true).elements, function(v){
21806 switch (this.viewMode) {
21809 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21815 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21822 Roo.each(this.picker().select('.next', true).elements, function(v){
21824 switch (this.viewMode) {
21827 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21833 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21841 moveMonth: function(date, dir)
21846 var new_date = new Date(date.valueOf()),
21847 day = new_date.getUTCDate(),
21848 month = new_date.getUTCMonth(),
21849 mag = Math.abs(dir),
21851 dir = dir > 0 ? 1 : -1;
21854 // If going back one month, make sure month is not current month
21855 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21857 return new_date.getUTCMonth() == month;
21859 // If going forward one month, make sure month is as expected
21860 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21862 return new_date.getUTCMonth() != new_month;
21864 new_month = month + dir;
21865 new_date.setUTCMonth(new_month);
21866 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21867 if (new_month < 0 || new_month > 11) {
21868 new_month = (new_month + 12) % 12;
21871 // For magnitudes >1, move one month at a time...
21872 for (var i=0; i<mag; i++) {
21873 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21874 new_date = this.moveMonth(new_date, dir);
21876 // ...then reset the day, keeping it in the new month
21877 new_month = new_date.getUTCMonth();
21878 new_date.setUTCDate(day);
21880 return new_month != new_date.getUTCMonth();
21883 // Common date-resetting loop -- if date is beyond end of month, make it
21886 new_date.setUTCDate(--day);
21887 new_date.setUTCMonth(new_month);
21892 moveYear: function(date, dir)
21894 return this.moveMonth(date, dir*12);
21897 dateWithinRange: function(date)
21899 return date >= this.startDate && date <= this.endDate;
21905 this.picker().remove();
21908 validateValue : function(value)
21910 if(this.getVisibilityEl().hasClass('hidden')){
21914 if(value.length < 1) {
21915 if(this.allowBlank){
21921 if(value.length < this.minLength){
21924 if(value.length > this.maxLength){
21928 var vt = Roo.form.VTypes;
21929 if(!vt[this.vtype](value, this)){
21933 if(typeof this.validator == "function"){
21934 var msg = this.validator(value);
21940 if(this.regex && !this.regex.test(value)){
21944 if(typeof(this.parseDate(value)) == 'undefined'){
21948 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21952 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21962 this.date = this.viewDate = '';
21964 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21969 Roo.apply(Roo.bootstrap.DateField, {
21980 html: '<i class="fa fa-arrow-left"/>'
21990 html: '<i class="fa fa-arrow-right"/>'
22032 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22033 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22034 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22035 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22036 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22049 navFnc: 'FullYear',
22054 navFnc: 'FullYear',
22059 Roo.apply(Roo.bootstrap.DateField, {
22063 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22067 cls: 'datepicker-days',
22071 cls: 'table-condensed',
22073 Roo.bootstrap.DateField.head,
22077 Roo.bootstrap.DateField.footer
22084 cls: 'datepicker-months',
22088 cls: 'table-condensed',
22090 Roo.bootstrap.DateField.head,
22091 Roo.bootstrap.DateField.content,
22092 Roo.bootstrap.DateField.footer
22099 cls: 'datepicker-years',
22103 cls: 'table-condensed',
22105 Roo.bootstrap.DateField.head,
22106 Roo.bootstrap.DateField.content,
22107 Roo.bootstrap.DateField.footer
22126 * @class Roo.bootstrap.TimeField
22127 * @extends Roo.bootstrap.Input
22128 * Bootstrap DateField class
22132 * Create a new TimeField
22133 * @param {Object} config The config object
22136 Roo.bootstrap.TimeField = function(config){
22137 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22141 * Fires when this field show.
22142 * @param {Roo.bootstrap.DateField} thisthis
22143 * @param {Mixed} date The date value
22148 * Fires when this field hide.
22149 * @param {Roo.bootstrap.DateField} this
22150 * @param {Mixed} date The date value
22155 * Fires when select a date.
22156 * @param {Roo.bootstrap.DateField} this
22157 * @param {Mixed} date The date value
22163 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22166 * @cfg {String} format
22167 * The default time format string which can be overriden for localization support. The format must be
22168 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22172 getAutoCreate : function()
22174 this.after = '<i class="fa far fa-clock"></i>';
22175 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22179 onRender: function(ct, position)
22182 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22184 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22186 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22188 this.pop = this.picker().select('>.datepicker-time',true).first();
22189 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22191 this.picker().on('mousedown', this.onMousedown, this);
22192 this.picker().on('click', this.onClick, this);
22194 this.picker().addClass('datepicker-dropdown');
22199 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22200 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22201 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22202 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22203 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22204 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22208 fireKey: function(e){
22209 if (!this.picker().isVisible()){
22210 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22216 e.preventDefault();
22224 this.onTogglePeriod();
22227 this.onIncrementMinutes();
22230 this.onDecrementMinutes();
22239 onClick: function(e) {
22240 e.stopPropagation();
22241 e.preventDefault();
22244 picker : function()
22246 return this.pickerEl;
22249 fillTime: function()
22251 var time = this.pop.select('tbody', true).first();
22253 time.dom.innerHTML = '';
22268 cls: 'hours-up fa fas fa-chevron-up'
22288 cls: 'minutes-up fa fas fa-chevron-up'
22309 cls: 'timepicker-hour',
22324 cls: 'timepicker-minute',
22339 cls: 'btn btn-primary period',
22361 cls: 'hours-down fa fas fa-chevron-down'
22381 cls: 'minutes-down fa fas fa-chevron-down'
22399 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22406 var hours = this.time.getHours();
22407 var minutes = this.time.getMinutes();
22420 hours = hours - 12;
22424 hours = '0' + hours;
22428 minutes = '0' + minutes;
22431 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22432 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22433 this.pop.select('button', true).first().dom.innerHTML = period;
22439 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22441 var cls = ['bottom'];
22443 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22450 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22454 //this.picker().setXY(20000,20000);
22455 this.picker().addClass(cls.join('-'));
22459 Roo.each(cls, function(c){
22464 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22465 //_this.picker().setTop(_this.inputEl().getHeight());
22469 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22471 //_this.picker().setTop(0 - _this.picker().getHeight());
22476 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22480 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22488 onFocus : function()
22490 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22494 onBlur : function()
22496 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22502 this.picker().show();
22507 this.fireEvent('show', this, this.date);
22512 this.picker().hide();
22515 this.fireEvent('hide', this, this.date);
22518 setTime : function()
22521 this.setValue(this.time.format(this.format));
22523 this.fireEvent('select', this, this.date);
22528 onMousedown: function(e){
22529 e.stopPropagation();
22530 e.preventDefault();
22533 onIncrementHours: function()
22535 Roo.log('onIncrementHours');
22536 this.time = this.time.add(Date.HOUR, 1);
22541 onDecrementHours: function()
22543 Roo.log('onDecrementHours');
22544 this.time = this.time.add(Date.HOUR, -1);
22548 onIncrementMinutes: function()
22550 Roo.log('onIncrementMinutes');
22551 this.time = this.time.add(Date.MINUTE, 1);
22555 onDecrementMinutes: function()
22557 Roo.log('onDecrementMinutes');
22558 this.time = this.time.add(Date.MINUTE, -1);
22562 onTogglePeriod: function()
22564 Roo.log('onTogglePeriod');
22565 this.time = this.time.add(Date.HOUR, 12);
22573 Roo.apply(Roo.bootstrap.TimeField, {
22577 cls: 'datepicker dropdown-menu',
22581 cls: 'datepicker-time',
22585 cls: 'table-condensed',
22614 cls: 'btn btn-info ok',
22642 * @class Roo.bootstrap.MonthField
22643 * @extends Roo.bootstrap.Input
22644 * Bootstrap MonthField class
22646 * @cfg {String} language default en
22649 * Create a new MonthField
22650 * @param {Object} config The config object
22653 Roo.bootstrap.MonthField = function(config){
22654 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22659 * Fires when this field show.
22660 * @param {Roo.bootstrap.MonthField} this
22661 * @param {Mixed} date The date value
22666 * Fires when this field hide.
22667 * @param {Roo.bootstrap.MonthField} this
22668 * @param {Mixed} date The date value
22673 * Fires when select a date.
22674 * @param {Roo.bootstrap.MonthField} this
22675 * @param {String} oldvalue The old value
22676 * @param {String} newvalue The new value
22682 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22684 onRender: function(ct, position)
22687 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22689 this.language = this.language || 'en';
22690 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22691 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22693 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22694 this.isInline = false;
22695 this.isInput = true;
22696 this.component = this.el.select('.add-on', true).first() || false;
22697 this.component = (this.component && this.component.length === 0) ? false : this.component;
22698 this.hasInput = this.component && this.inputEL().length;
22700 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22702 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22704 this.picker().on('mousedown', this.onMousedown, this);
22705 this.picker().on('click', this.onClick, this);
22707 this.picker().addClass('datepicker-dropdown');
22709 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22710 v.setStyle('width', '189px');
22717 if(this.isInline) {
22723 setValue: function(v, suppressEvent)
22725 var o = this.getValue();
22727 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22731 if(suppressEvent !== true){
22732 this.fireEvent('select', this, o, v);
22737 getValue: function()
22742 onClick: function(e)
22744 e.stopPropagation();
22745 e.preventDefault();
22747 var target = e.getTarget();
22749 if(target.nodeName.toLowerCase() === 'i'){
22750 target = Roo.get(target).dom.parentNode;
22753 var nodeName = target.nodeName;
22754 var className = target.className;
22755 var html = target.innerHTML;
22757 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22761 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22763 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22769 picker : function()
22771 return this.pickerEl;
22774 fillMonths: function()
22777 var months = this.picker().select('>.datepicker-months td', true).first();
22779 months.dom.innerHTML = '';
22785 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22788 months.createChild(month);
22797 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22798 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22801 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22802 e.removeClass('active');
22804 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22805 e.addClass('active');
22812 if(this.isInline) {
22816 this.picker().removeClass(['bottom', 'top']);
22818 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22820 * place to the top of element!
22824 this.picker().addClass('top');
22825 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22830 this.picker().addClass('bottom');
22832 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22835 onFocus : function()
22837 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22841 onBlur : function()
22843 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22845 var d = this.inputEl().getValue();
22854 this.picker().show();
22855 this.picker().select('>.datepicker-months', true).first().show();
22859 this.fireEvent('show', this, this.date);
22864 if(this.isInline) {
22867 this.picker().hide();
22868 this.fireEvent('hide', this, this.date);
22872 onMousedown: function(e)
22874 e.stopPropagation();
22875 e.preventDefault();
22880 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22884 fireKey: function(e)
22886 if (!this.picker().isVisible()){
22887 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22898 e.preventDefault();
22902 dir = e.keyCode == 37 ? -1 : 1;
22904 this.vIndex = this.vIndex + dir;
22906 if(this.vIndex < 0){
22910 if(this.vIndex > 11){
22914 if(isNaN(this.vIndex)){
22918 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22924 dir = e.keyCode == 38 ? -1 : 1;
22926 this.vIndex = this.vIndex + dir * 4;
22928 if(this.vIndex < 0){
22932 if(this.vIndex > 11){
22936 if(isNaN(this.vIndex)){
22940 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22945 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22946 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22950 e.preventDefault();
22953 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22954 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22970 this.picker().remove();
22975 Roo.apply(Roo.bootstrap.MonthField, {
22994 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22995 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23000 Roo.apply(Roo.bootstrap.MonthField, {
23004 cls: 'datepicker dropdown-menu roo-dynamic',
23008 cls: 'datepicker-months',
23012 cls: 'table-condensed',
23014 Roo.bootstrap.DateField.content
23034 * @class Roo.bootstrap.CheckBox
23035 * @extends Roo.bootstrap.Input
23036 * Bootstrap CheckBox class
23038 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23039 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23040 * @cfg {String} boxLabel The text that appears beside the checkbox
23041 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23042 * @cfg {Boolean} checked initnal the element
23043 * @cfg {Boolean} inline inline the element (default false)
23044 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23045 * @cfg {String} tooltip label tooltip
23048 * Create a new CheckBox
23049 * @param {Object} config The config object
23052 Roo.bootstrap.CheckBox = function(config){
23053 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23058 * Fires when the element is checked or unchecked.
23059 * @param {Roo.bootstrap.CheckBox} this This input
23060 * @param {Boolean} checked The new checked value
23065 * Fires when the element is click.
23066 * @param {Roo.bootstrap.CheckBox} this This input
23073 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23075 inputType: 'checkbox',
23084 // checkbox success does not make any sense really..
23089 getAutoCreate : function()
23091 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23097 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23100 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23106 type : this.inputType,
23107 value : this.inputValue,
23108 cls : 'roo-' + this.inputType, //'form-box',
23109 placeholder : this.placeholder || ''
23113 if(this.inputType != 'radio'){
23117 cls : 'roo-hidden-value',
23118 value : this.checked ? this.inputValue : this.valueOff
23123 if (this.weight) { // Validity check?
23124 cfg.cls += " " + this.inputType + "-" + this.weight;
23127 if (this.disabled) {
23128 input.disabled=true;
23132 input.checked = this.checked;
23137 input.name = this.name;
23139 if(this.inputType != 'radio'){
23140 hidden.name = this.name;
23141 input.name = '_hidden_' + this.name;
23146 input.cls += ' input-' + this.size;
23151 ['xs','sm','md','lg'].map(function(size){
23152 if (settings[size]) {
23153 cfg.cls += ' col-' + size + '-' + settings[size];
23157 var inputblock = input;
23159 if (this.before || this.after) {
23162 cls : 'input-group',
23167 inputblock.cn.push({
23169 cls : 'input-group-addon',
23174 inputblock.cn.push(input);
23176 if(this.inputType != 'radio'){
23177 inputblock.cn.push(hidden);
23181 inputblock.cn.push({
23183 cls : 'input-group-addon',
23189 var boxLabelCfg = false;
23195 //'for': id, // box label is handled by onclick - so no for...
23197 html: this.boxLabel
23200 boxLabelCfg.tooltip = this.tooltip;
23206 if (align ==='left' && this.fieldLabel.length) {
23207 // Roo.log("left and has label");
23212 cls : 'control-label',
23213 html : this.fieldLabel
23224 cfg.cn[1].cn.push(boxLabelCfg);
23227 if(this.labelWidth > 12){
23228 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23231 if(this.labelWidth < 13 && this.labelmd == 0){
23232 this.labelmd = this.labelWidth;
23235 if(this.labellg > 0){
23236 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23237 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23240 if(this.labelmd > 0){
23241 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23242 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23245 if(this.labelsm > 0){
23246 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23247 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23250 if(this.labelxs > 0){
23251 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23252 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23255 } else if ( this.fieldLabel.length) {
23256 // Roo.log(" label");
23260 tag: this.boxLabel ? 'span' : 'label',
23262 cls: 'control-label box-input-label',
23263 //cls : 'input-group-addon',
23264 html : this.fieldLabel
23271 cfg.cn.push(boxLabelCfg);
23276 // Roo.log(" no label && no align");
23277 cfg.cn = [ inputblock ] ;
23279 cfg.cn.push(boxLabelCfg);
23287 if(this.inputType != 'radio'){
23288 cfg.cn.push(hidden);
23296 * return the real input element.
23298 inputEl: function ()
23300 return this.el.select('input.roo-' + this.inputType,true).first();
23302 hiddenEl: function ()
23304 return this.el.select('input.roo-hidden-value',true).first();
23307 labelEl: function()
23309 return this.el.select('label.control-label',true).first();
23311 /* depricated... */
23315 return this.labelEl();
23318 boxLabelEl: function()
23320 return this.el.select('label.box-label',true).first();
23323 initEvents : function()
23325 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23327 this.inputEl().on('click', this.onClick, this);
23329 if (this.boxLabel) {
23330 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23333 this.startValue = this.getValue();
23336 Roo.bootstrap.CheckBox.register(this);
23340 onClick : function(e)
23342 if(this.fireEvent('click', this, e) !== false){
23343 this.setChecked(!this.checked);
23348 setChecked : function(state,suppressEvent)
23350 this.startValue = this.getValue();
23352 if(this.inputType == 'radio'){
23354 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23355 e.dom.checked = false;
23358 this.inputEl().dom.checked = true;
23360 this.inputEl().dom.value = this.inputValue;
23362 if(suppressEvent !== true){
23363 this.fireEvent('check', this, true);
23371 this.checked = state;
23373 this.inputEl().dom.checked = state;
23376 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23378 if(suppressEvent !== true){
23379 this.fireEvent('check', this, state);
23385 getValue : function()
23387 if(this.inputType == 'radio'){
23388 return this.getGroupValue();
23391 return this.hiddenEl().dom.value;
23395 getGroupValue : function()
23397 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23401 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23404 setValue : function(v,suppressEvent)
23406 if(this.inputType == 'radio'){
23407 this.setGroupValue(v, suppressEvent);
23411 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23416 setGroupValue : function(v, suppressEvent)
23418 this.startValue = this.getValue();
23420 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23421 e.dom.checked = false;
23423 if(e.dom.value == v){
23424 e.dom.checked = true;
23428 if(suppressEvent !== true){
23429 this.fireEvent('check', this, true);
23437 validate : function()
23439 if(this.getVisibilityEl().hasClass('hidden')){
23445 (this.inputType == 'radio' && this.validateRadio()) ||
23446 (this.inputType == 'checkbox' && this.validateCheckbox())
23452 this.markInvalid();
23456 validateRadio : function()
23458 if(this.getVisibilityEl().hasClass('hidden')){
23462 if(this.allowBlank){
23468 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23469 if(!e.dom.checked){
23481 validateCheckbox : function()
23484 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23485 //return (this.getValue() == this.inputValue) ? true : false;
23488 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23496 for(var i in group){
23497 if(group[i].el.isVisible(true)){
23505 for(var i in group){
23510 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23517 * Mark this field as valid
23519 markValid : function()
23523 this.fireEvent('valid', this);
23525 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23528 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23535 if(this.inputType == 'radio'){
23536 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23537 var fg = e.findParent('.form-group', false, true);
23538 if (Roo.bootstrap.version == 3) {
23539 fg.removeClass([_this.invalidClass, _this.validClass]);
23540 fg.addClass(_this.validClass);
23542 fg.removeClass(['is-valid', 'is-invalid']);
23543 fg.addClass('is-valid');
23551 var fg = this.el.findParent('.form-group', false, true);
23552 if (Roo.bootstrap.version == 3) {
23553 fg.removeClass([this.invalidClass, this.validClass]);
23554 fg.addClass(this.validClass);
23556 fg.removeClass(['is-valid', 'is-invalid']);
23557 fg.addClass('is-valid');
23562 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23568 for(var i in group){
23569 var fg = group[i].el.findParent('.form-group', false, true);
23570 if (Roo.bootstrap.version == 3) {
23571 fg.removeClass([this.invalidClass, this.validClass]);
23572 fg.addClass(this.validClass);
23574 fg.removeClass(['is-valid', 'is-invalid']);
23575 fg.addClass('is-valid');
23581 * Mark this field as invalid
23582 * @param {String} msg The validation message
23584 markInvalid : function(msg)
23586 if(this.allowBlank){
23592 this.fireEvent('invalid', this, msg);
23594 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23597 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23601 label.markInvalid();
23604 if(this.inputType == 'radio'){
23606 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23607 var fg = e.findParent('.form-group', false, true);
23608 if (Roo.bootstrap.version == 3) {
23609 fg.removeClass([_this.invalidClass, _this.validClass]);
23610 fg.addClass(_this.invalidClass);
23612 fg.removeClass(['is-invalid', 'is-valid']);
23613 fg.addClass('is-invalid');
23621 var fg = this.el.findParent('.form-group', false, true);
23622 if (Roo.bootstrap.version == 3) {
23623 fg.removeClass([_this.invalidClass, _this.validClass]);
23624 fg.addClass(_this.invalidClass);
23626 fg.removeClass(['is-invalid', 'is-valid']);
23627 fg.addClass('is-invalid');
23632 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23638 for(var i in group){
23639 var fg = group[i].el.findParent('.form-group', false, true);
23640 if (Roo.bootstrap.version == 3) {
23641 fg.removeClass([_this.invalidClass, _this.validClass]);
23642 fg.addClass(_this.invalidClass);
23644 fg.removeClass(['is-invalid', 'is-valid']);
23645 fg.addClass('is-invalid');
23651 clearInvalid : function()
23653 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23655 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23657 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23659 if (label && label.iconEl) {
23660 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23661 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23665 disable : function()
23667 if(this.inputType != 'radio'){
23668 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23675 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23676 _this.getActionEl().addClass(this.disabledClass);
23677 e.dom.disabled = true;
23681 this.disabled = true;
23682 this.fireEvent("disable", this);
23686 enable : function()
23688 if(this.inputType != 'radio'){
23689 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23696 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23697 _this.getActionEl().removeClass(this.disabledClass);
23698 e.dom.disabled = false;
23702 this.disabled = false;
23703 this.fireEvent("enable", this);
23707 setBoxLabel : function(v)
23712 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23718 Roo.apply(Roo.bootstrap.CheckBox, {
23723 * register a CheckBox Group
23724 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23726 register : function(checkbox)
23728 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23729 this.groups[checkbox.groupId] = {};
23732 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23736 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23740 * fetch a CheckBox Group based on the group ID
23741 * @param {string} the group ID
23742 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23744 get: function(groupId) {
23745 if (typeof(this.groups[groupId]) == 'undefined') {
23749 return this.groups[groupId] ;
23762 * @class Roo.bootstrap.Radio
23763 * @extends Roo.bootstrap.Component
23764 * Bootstrap Radio class
23765 * @cfg {String} boxLabel - the label associated
23766 * @cfg {String} value - the value of radio
23769 * Create a new Radio
23770 * @param {Object} config The config object
23772 Roo.bootstrap.Radio = function(config){
23773 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23777 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23783 getAutoCreate : function()
23787 cls : 'form-group radio',
23792 html : this.boxLabel
23800 initEvents : function()
23802 this.parent().register(this);
23804 this.el.on('click', this.onClick, this);
23808 onClick : function(e)
23810 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23811 this.setChecked(true);
23815 setChecked : function(state, suppressEvent)
23817 this.parent().setValue(this.value, suppressEvent);
23821 setBoxLabel : function(v)
23826 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23841 * @class Roo.bootstrap.SecurePass
23842 * @extends Roo.bootstrap.Input
23843 * Bootstrap SecurePass class
23847 * Create a new SecurePass
23848 * @param {Object} config The config object
23851 Roo.bootstrap.SecurePass = function (config) {
23852 // these go here, so the translation tool can replace them..
23854 PwdEmpty: "Please type a password, and then retype it to confirm.",
23855 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23856 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23857 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23858 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23859 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23860 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23861 TooWeak: "Your password is Too Weak."
23863 this.meterLabel = "Password strength:";
23864 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23865 this.meterClass = [
23866 "roo-password-meter-tooweak",
23867 "roo-password-meter-weak",
23868 "roo-password-meter-medium",
23869 "roo-password-meter-strong",
23870 "roo-password-meter-grey"
23875 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23878 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23880 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23882 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23883 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23884 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23885 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23886 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23887 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23888 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23898 * @cfg {String/Object} Label for the strength meter (defaults to
23899 * 'Password strength:')
23904 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23905 * ['Weak', 'Medium', 'Strong'])
23908 pwdStrengths: false,
23921 initEvents: function ()
23923 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23925 if (this.el.is('input[type=password]') && Roo.isSafari) {
23926 this.el.on('keydown', this.SafariOnKeyDown, this);
23929 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23932 onRender: function (ct, position)
23934 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23935 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23936 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23938 this.trigger.createChild({
23943 cls: 'roo-password-meter-grey col-xs-12',
23946 //width: this.meterWidth + 'px'
23950 cls: 'roo-password-meter-text'
23956 if (this.hideTrigger) {
23957 this.trigger.setDisplayed(false);
23959 this.setSize(this.width || '', this.height || '');
23962 onDestroy: function ()
23964 if (this.trigger) {
23965 this.trigger.removeAllListeners();
23966 this.trigger.remove();
23969 this.wrap.remove();
23971 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23974 checkStrength: function ()
23976 var pwd = this.inputEl().getValue();
23977 if (pwd == this._lastPwd) {
23982 if (this.ClientSideStrongPassword(pwd)) {
23984 } else if (this.ClientSideMediumPassword(pwd)) {
23986 } else if (this.ClientSideWeakPassword(pwd)) {
23992 Roo.log('strength1: ' + strength);
23994 //var pm = this.trigger.child('div/div/div').dom;
23995 var pm = this.trigger.child('div/div');
23996 pm.removeClass(this.meterClass);
23997 pm.addClass(this.meterClass[strength]);
24000 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24002 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24004 this._lastPwd = pwd;
24008 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24010 this._lastPwd = '';
24012 var pm = this.trigger.child('div/div');
24013 pm.removeClass(this.meterClass);
24014 pm.addClass('roo-password-meter-grey');
24017 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24020 this.inputEl().dom.type='password';
24023 validateValue: function (value)
24025 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24028 if (value.length == 0) {
24029 if (this.allowBlank) {
24030 this.clearInvalid();
24034 this.markInvalid(this.errors.PwdEmpty);
24035 this.errorMsg = this.errors.PwdEmpty;
24043 if (!value.match(/[\x21-\x7e]+/)) {
24044 this.markInvalid(this.errors.PwdBadChar);
24045 this.errorMsg = this.errors.PwdBadChar;
24048 if (value.length < 6) {
24049 this.markInvalid(this.errors.PwdShort);
24050 this.errorMsg = this.errors.PwdShort;
24053 if (value.length > 16) {
24054 this.markInvalid(this.errors.PwdLong);
24055 this.errorMsg = this.errors.PwdLong;
24059 if (this.ClientSideStrongPassword(value)) {
24061 } else if (this.ClientSideMediumPassword(value)) {
24063 } else if (this.ClientSideWeakPassword(value)) {
24070 if (strength < 2) {
24071 //this.markInvalid(this.errors.TooWeak);
24072 this.errorMsg = this.errors.TooWeak;
24077 console.log('strength2: ' + strength);
24079 //var pm = this.trigger.child('div/div/div').dom;
24081 var pm = this.trigger.child('div/div');
24082 pm.removeClass(this.meterClass);
24083 pm.addClass(this.meterClass[strength]);
24085 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24087 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24089 this.errorMsg = '';
24093 CharacterSetChecks: function (type)
24096 this.fResult = false;
24099 isctype: function (character, type)
24102 case this.kCapitalLetter:
24103 if (character >= 'A' && character <= 'Z') {
24108 case this.kSmallLetter:
24109 if (character >= 'a' && character <= 'z') {
24115 if (character >= '0' && character <= '9') {
24120 case this.kPunctuation:
24121 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24132 IsLongEnough: function (pwd, size)
24134 return !(pwd == null || isNaN(size) || pwd.length < size);
24137 SpansEnoughCharacterSets: function (word, nb)
24139 if (!this.IsLongEnough(word, nb))
24144 var characterSetChecks = new Array(
24145 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24146 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24149 for (var index = 0; index < word.length; ++index) {
24150 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24151 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24152 characterSetChecks[nCharSet].fResult = true;
24159 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24160 if (characterSetChecks[nCharSet].fResult) {
24165 if (nCharSets < nb) {
24171 ClientSideStrongPassword: function (pwd)
24173 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24176 ClientSideMediumPassword: function (pwd)
24178 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24181 ClientSideWeakPassword: function (pwd)
24183 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24186 })//<script type="text/javascript">
24189 * Based Ext JS Library 1.1.1
24190 * Copyright(c) 2006-2007, Ext JS, LLC.
24196 * @class Roo.HtmlEditorCore
24197 * @extends Roo.Component
24198 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24200 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24203 Roo.HtmlEditorCore = function(config){
24206 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24211 * @event initialize
24212 * Fires when the editor is fully initialized (including the iframe)
24213 * @param {Roo.HtmlEditorCore} this
24218 * Fires when the editor is first receives the focus. Any insertion must wait
24219 * until after this event.
24220 * @param {Roo.HtmlEditorCore} this
24224 * @event beforesync
24225 * Fires before the textarea is updated with content from the editor iframe. Return false
24226 * to cancel the sync.
24227 * @param {Roo.HtmlEditorCore} this
24228 * @param {String} html
24232 * @event beforepush
24233 * Fires before the iframe editor is updated with content from the textarea. Return false
24234 * to cancel the push.
24235 * @param {Roo.HtmlEditorCore} this
24236 * @param {String} html
24241 * Fires when the textarea is updated with content from the editor iframe.
24242 * @param {Roo.HtmlEditorCore} this
24243 * @param {String} html
24248 * Fires when the iframe editor is updated with content from the textarea.
24249 * @param {Roo.HtmlEditorCore} this
24250 * @param {String} html
24255 * @event editorevent
24256 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24257 * @param {Roo.HtmlEditorCore} this
24263 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24265 // defaults : white / black...
24266 this.applyBlacklists();
24273 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24277 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24283 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24288 * @cfg {Number} height (in pixels)
24292 * @cfg {Number} width (in pixels)
24297 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24300 stylesheets: false,
24305 // private properties
24306 validationEvent : false,
24308 initialized : false,
24310 sourceEditMode : false,
24311 onFocus : Roo.emptyFn,
24313 hideMode:'offsets',
24317 // blacklist + whitelisted elements..
24324 * Protected method that will not generally be called directly. It
24325 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24326 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24328 getDocMarkup : function(){
24332 // inherit styels from page...??
24333 if (this.stylesheets === false) {
24335 Roo.get(document.head).select('style').each(function(node) {
24336 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24339 Roo.get(document.head).select('link').each(function(node) {
24340 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24343 } else if (!this.stylesheets.length) {
24345 st = '<style type="text/css">' +
24346 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24349 for (var i in this.stylesheets) {
24350 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24355 st += '<style type="text/css">' +
24356 'IMG { cursor: pointer } ' +
24359 var cls = 'roo-htmleditor-body';
24361 if(this.bodyCls.length){
24362 cls += ' ' + this.bodyCls;
24365 return '<html><head>' + st +
24366 //<style type="text/css">' +
24367 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24369 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24373 onRender : function(ct, position)
24376 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24377 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24380 this.el.dom.style.border = '0 none';
24381 this.el.dom.setAttribute('tabIndex', -1);
24382 this.el.addClass('x-hidden hide');
24386 if(Roo.isIE){ // fix IE 1px bogus margin
24387 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24391 this.frameId = Roo.id();
24395 var iframe = this.owner.wrap.createChild({
24397 cls: 'form-control', // bootstrap..
24399 name: this.frameId,
24400 frameBorder : 'no',
24401 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24406 this.iframe = iframe.dom;
24408 this.assignDocWin();
24410 this.doc.designMode = 'on';
24413 this.doc.write(this.getDocMarkup());
24417 var task = { // must defer to wait for browser to be ready
24419 //console.log("run task?" + this.doc.readyState);
24420 this.assignDocWin();
24421 if(this.doc.body || this.doc.readyState == 'complete'){
24423 this.doc.designMode="on";
24427 Roo.TaskMgr.stop(task);
24428 this.initEditor.defer(10, this);
24435 Roo.TaskMgr.start(task);
24440 onResize : function(w, h)
24442 Roo.log('resize: ' +w + ',' + h );
24443 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24447 if(typeof w == 'number'){
24449 this.iframe.style.width = w + 'px';
24451 if(typeof h == 'number'){
24453 this.iframe.style.height = h + 'px';
24455 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24462 * Toggles the editor between standard and source edit mode.
24463 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24465 toggleSourceEdit : function(sourceEditMode){
24467 this.sourceEditMode = sourceEditMode === true;
24469 if(this.sourceEditMode){
24471 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24474 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24475 //this.iframe.className = '';
24478 //this.setSize(this.owner.wrap.getSize());
24479 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24486 * Protected method that will not generally be called directly. If you need/want
24487 * custom HTML cleanup, this is the method you should override.
24488 * @param {String} html The HTML to be cleaned
24489 * return {String} The cleaned HTML
24491 cleanHtml : function(html){
24492 html = String(html);
24493 if(html.length > 5){
24494 if(Roo.isSafari){ // strip safari nonsense
24495 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24498 if(html == ' '){
24505 * HTML Editor -> Textarea
24506 * Protected method that will not generally be called directly. Syncs the contents
24507 * of the editor iframe with the textarea.
24509 syncValue : function(){
24510 if(this.initialized){
24511 var bd = (this.doc.body || this.doc.documentElement);
24512 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24513 var html = bd.innerHTML;
24515 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24516 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24518 html = '<div style="'+m[0]+'">' + html + '</div>';
24521 html = this.cleanHtml(html);
24522 // fix up the special chars.. normaly like back quotes in word...
24523 // however we do not want to do this with chinese..
24524 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24526 var cc = match.charCodeAt();
24528 // Get the character value, handling surrogate pairs
24529 if (match.length == 2) {
24530 // It's a surrogate pair, calculate the Unicode code point
24531 var high = match.charCodeAt(0) - 0xD800;
24532 var low = match.charCodeAt(1) - 0xDC00;
24533 cc = (high * 0x400) + low + 0x10000;
24535 (cc >= 0x4E00 && cc < 0xA000 ) ||
24536 (cc >= 0x3400 && cc < 0x4E00 ) ||
24537 (cc >= 0xf900 && cc < 0xfb00 )
24542 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24543 return "&#" + cc + ";";
24550 if(this.owner.fireEvent('beforesync', this, html) !== false){
24551 this.el.dom.value = html;
24552 this.owner.fireEvent('sync', this, html);
24558 * Protected method that will not generally be called directly. Pushes the value of the textarea
24559 * into the iframe editor.
24561 pushValue : function(){
24562 if(this.initialized){
24563 var v = this.el.dom.value.trim();
24565 // if(v.length < 1){
24569 if(this.owner.fireEvent('beforepush', this, v) !== false){
24570 var d = (this.doc.body || this.doc.documentElement);
24572 this.cleanUpPaste();
24573 this.el.dom.value = d.innerHTML;
24574 this.owner.fireEvent('push', this, v);
24580 deferFocus : function(){
24581 this.focus.defer(10, this);
24585 focus : function(){
24586 if(this.win && !this.sourceEditMode){
24593 assignDocWin: function()
24595 var iframe = this.iframe;
24598 this.doc = iframe.contentWindow.document;
24599 this.win = iframe.contentWindow;
24601 // if (!Roo.get(this.frameId)) {
24604 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24605 // this.win = Roo.get(this.frameId).dom.contentWindow;
24607 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24611 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24612 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24617 initEditor : function(){
24618 //console.log("INIT EDITOR");
24619 this.assignDocWin();
24623 this.doc.designMode="on";
24625 this.doc.write(this.getDocMarkup());
24628 var dbody = (this.doc.body || this.doc.documentElement);
24629 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24630 // this copies styles from the containing element into thsi one..
24631 // not sure why we need all of this..
24632 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24634 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24635 //ss['background-attachment'] = 'fixed'; // w3c
24636 dbody.bgProperties = 'fixed'; // ie
24637 //Roo.DomHelper.applyStyles(dbody, ss);
24638 Roo.EventManager.on(this.doc, {
24639 //'mousedown': this.onEditorEvent,
24640 'mouseup': this.onEditorEvent,
24641 'dblclick': this.onEditorEvent,
24642 'click': this.onEditorEvent,
24643 'keyup': this.onEditorEvent,
24648 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24650 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24651 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24653 this.initialized = true;
24655 this.owner.fireEvent('initialize', this);
24660 onDestroy : function(){
24666 //for (var i =0; i < this.toolbars.length;i++) {
24667 // // fixme - ask toolbars for heights?
24668 // this.toolbars[i].onDestroy();
24671 //this.wrap.dom.innerHTML = '';
24672 //this.wrap.remove();
24677 onFirstFocus : function(){
24679 this.assignDocWin();
24682 this.activated = true;
24685 if(Roo.isGecko){ // prevent silly gecko errors
24687 var s = this.win.getSelection();
24688 if(!s.focusNode || s.focusNode.nodeType != 3){
24689 var r = s.getRangeAt(0);
24690 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24695 this.execCmd('useCSS', true);
24696 this.execCmd('styleWithCSS', false);
24699 this.owner.fireEvent('activate', this);
24703 adjustFont: function(btn){
24704 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24705 //if(Roo.isSafari){ // safari
24708 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24709 if(Roo.isSafari){ // safari
24710 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24711 v = (v < 10) ? 10 : v;
24712 v = (v > 48) ? 48 : v;
24713 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24718 v = Math.max(1, v+adjust);
24720 this.execCmd('FontSize', v );
24723 onEditorEvent : function(e)
24725 this.owner.fireEvent('editorevent', this, e);
24726 // this.updateToolbar();
24727 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24730 insertTag : function(tg)
24732 // could be a bit smarter... -> wrap the current selected tRoo..
24733 if (tg.toLowerCase() == 'span' ||
24734 tg.toLowerCase() == 'code' ||
24735 tg.toLowerCase() == 'sup' ||
24736 tg.toLowerCase() == 'sub'
24739 range = this.createRange(this.getSelection());
24740 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24741 wrappingNode.appendChild(range.extractContents());
24742 range.insertNode(wrappingNode);
24749 this.execCmd("formatblock", tg);
24753 insertText : function(txt)
24757 var range = this.createRange();
24758 range.deleteContents();
24759 //alert(Sender.getAttribute('label'));
24761 range.insertNode(this.doc.createTextNode(txt));
24767 * Executes a Midas editor command on the editor document and performs necessary focus and
24768 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24769 * @param {String} cmd The Midas command
24770 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24772 relayCmd : function(cmd, value){
24774 this.execCmd(cmd, value);
24775 this.owner.fireEvent('editorevent', this);
24776 //this.updateToolbar();
24777 this.owner.deferFocus();
24781 * Executes a Midas editor command directly on the editor document.
24782 * For visual commands, you should use {@link #relayCmd} instead.
24783 * <b>This should only be called after the editor is initialized.</b>
24784 * @param {String} cmd The Midas command
24785 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24787 execCmd : function(cmd, value){
24788 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24795 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24797 * @param {String} text | dom node..
24799 insertAtCursor : function(text)
24802 if(!this.activated){
24808 var r = this.doc.selection.createRange();
24819 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24823 // from jquery ui (MIT licenced)
24825 var win = this.win;
24827 if (win.getSelection && win.getSelection().getRangeAt) {
24828 range = win.getSelection().getRangeAt(0);
24829 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24830 range.insertNode(node);
24831 } else if (win.document.selection && win.document.selection.createRange) {
24832 // no firefox support
24833 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24834 win.document.selection.createRange().pasteHTML(txt);
24836 // no firefox support
24837 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24838 this.execCmd('InsertHTML', txt);
24847 mozKeyPress : function(e){
24849 var c = e.getCharCode(), cmd;
24852 c = String.fromCharCode(c).toLowerCase();
24866 this.cleanUpPaste.defer(100, this);
24874 e.preventDefault();
24882 fixKeys : function(){ // load time branching for fastest keydown performance
24884 return function(e){
24885 var k = e.getKey(), r;
24888 r = this.doc.selection.createRange();
24891 r.pasteHTML('    ');
24898 r = this.doc.selection.createRange();
24900 var target = r.parentElement();
24901 if(!target || target.tagName.toLowerCase() != 'li'){
24903 r.pasteHTML('<br />');
24909 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24910 this.cleanUpPaste.defer(100, this);
24916 }else if(Roo.isOpera){
24917 return function(e){
24918 var k = e.getKey();
24922 this.execCmd('InsertHTML','    ');
24925 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24926 this.cleanUpPaste.defer(100, this);
24931 }else if(Roo.isSafari){
24932 return function(e){
24933 var k = e.getKey();
24937 this.execCmd('InsertText','\t');
24941 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24942 this.cleanUpPaste.defer(100, this);
24950 getAllAncestors: function()
24952 var p = this.getSelectedNode();
24955 a.push(p); // push blank onto stack..
24956 p = this.getParentElement();
24960 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24964 a.push(this.doc.body);
24968 lastSelNode : false,
24971 getSelection : function()
24973 this.assignDocWin();
24974 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24977 getSelectedNode: function()
24979 // this may only work on Gecko!!!
24981 // should we cache this!!!!
24986 var range = this.createRange(this.getSelection()).cloneRange();
24989 var parent = range.parentElement();
24991 var testRange = range.duplicate();
24992 testRange.moveToElementText(parent);
24993 if (testRange.inRange(range)) {
24996 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24999 parent = parent.parentElement;
25004 // is ancestor a text element.
25005 var ac = range.commonAncestorContainer;
25006 if (ac.nodeType == 3) {
25007 ac = ac.parentNode;
25010 var ar = ac.childNodes;
25013 var other_nodes = [];
25014 var has_other_nodes = false;
25015 for (var i=0;i<ar.length;i++) {
25016 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25019 // fullly contained node.
25021 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25026 // probably selected..
25027 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25028 other_nodes.push(ar[i]);
25032 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25037 has_other_nodes = true;
25039 if (!nodes.length && other_nodes.length) {
25040 nodes= other_nodes;
25042 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25048 createRange: function(sel)
25050 // this has strange effects when using with
25051 // top toolbar - not sure if it's a great idea.
25052 //this.editor.contentWindow.focus();
25053 if (typeof sel != "undefined") {
25055 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25057 return this.doc.createRange();
25060 return this.doc.createRange();
25063 getParentElement: function()
25066 this.assignDocWin();
25067 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25069 var range = this.createRange(sel);
25072 var p = range.commonAncestorContainer;
25073 while (p.nodeType == 3) { // text node
25084 * Range intersection.. the hard stuff...
25088 * [ -- selected range --- ]
25092 * if end is before start or hits it. fail.
25093 * if start is after end or hits it fail.
25095 * if either hits (but other is outside. - then it's not
25101 // @see http://www.thismuchiknow.co.uk/?p=64.
25102 rangeIntersectsNode : function(range, node)
25104 var nodeRange = node.ownerDocument.createRange();
25106 nodeRange.selectNode(node);
25108 nodeRange.selectNodeContents(node);
25111 var rangeStartRange = range.cloneRange();
25112 rangeStartRange.collapse(true);
25114 var rangeEndRange = range.cloneRange();
25115 rangeEndRange.collapse(false);
25117 var nodeStartRange = nodeRange.cloneRange();
25118 nodeStartRange.collapse(true);
25120 var nodeEndRange = nodeRange.cloneRange();
25121 nodeEndRange.collapse(false);
25123 return rangeStartRange.compareBoundaryPoints(
25124 Range.START_TO_START, nodeEndRange) == -1 &&
25125 rangeEndRange.compareBoundaryPoints(
25126 Range.START_TO_START, nodeStartRange) == 1;
25130 rangeCompareNode : function(range, node)
25132 var nodeRange = node.ownerDocument.createRange();
25134 nodeRange.selectNode(node);
25136 nodeRange.selectNodeContents(node);
25140 range.collapse(true);
25142 nodeRange.collapse(true);
25144 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25145 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25147 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25149 var nodeIsBefore = ss == 1;
25150 var nodeIsAfter = ee == -1;
25152 if (nodeIsBefore && nodeIsAfter) {
25155 if (!nodeIsBefore && nodeIsAfter) {
25156 return 1; //right trailed.
25159 if (nodeIsBefore && !nodeIsAfter) {
25160 return 2; // left trailed.
25166 // private? - in a new class?
25167 cleanUpPaste : function()
25169 // cleans up the whole document..
25170 Roo.log('cleanuppaste');
25172 this.cleanUpChildren(this.doc.body);
25173 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25174 if (clean != this.doc.body.innerHTML) {
25175 this.doc.body.innerHTML = clean;
25180 cleanWordChars : function(input) {// change the chars to hex code
25181 var he = Roo.HtmlEditorCore;
25183 var output = input;
25184 Roo.each(he.swapCodes, function(sw) {
25185 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25187 output = output.replace(swapper, sw[1]);
25194 cleanUpChildren : function (n)
25196 if (!n.childNodes.length) {
25199 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25200 this.cleanUpChild(n.childNodes[i]);
25207 cleanUpChild : function (node)
25210 //console.log(node);
25211 if (node.nodeName == "#text") {
25212 // clean up silly Windows -- stuff?
25215 if (node.nodeName == "#comment") {
25216 node.parentNode.removeChild(node);
25217 // clean up silly Windows -- stuff?
25220 var lcname = node.tagName.toLowerCase();
25221 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25222 // whitelist of tags..
25224 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25226 node.parentNode.removeChild(node);
25231 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25233 // spans with no attributes - just remove them..
25234 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25235 remove_keep_children = true;
25238 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25239 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25241 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25242 // remove_keep_children = true;
25245 if (remove_keep_children) {
25246 this.cleanUpChildren(node);
25247 // inserts everything just before this node...
25248 while (node.childNodes.length) {
25249 var cn = node.childNodes[0];
25250 node.removeChild(cn);
25251 node.parentNode.insertBefore(cn, node);
25253 node.parentNode.removeChild(node);
25257 if (!node.attributes || !node.attributes.length) {
25262 this.cleanUpChildren(node);
25266 function cleanAttr(n,v)
25269 if (v.match(/^\./) || v.match(/^\//)) {
25272 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25275 if (v.match(/^#/)) {
25278 if (v.match(/^\{/)) { // allow template editing.
25281 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25282 node.removeAttribute(n);
25286 var cwhite = this.cwhite;
25287 var cblack = this.cblack;
25289 function cleanStyle(n,v)
25291 if (v.match(/expression/)) { //XSS?? should we even bother..
25292 node.removeAttribute(n);
25296 var parts = v.split(/;/);
25299 Roo.each(parts, function(p) {
25300 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25304 var l = p.split(':').shift().replace(/\s+/g,'');
25305 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25307 if ( cwhite.length && cblack.indexOf(l) > -1) {
25308 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25309 //node.removeAttribute(n);
25313 // only allow 'c whitelisted system attributes'
25314 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25315 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25316 //node.removeAttribute(n);
25326 if (clean.length) {
25327 node.setAttribute(n, clean.join(';'));
25329 node.removeAttribute(n);
25335 for (var i = node.attributes.length-1; i > -1 ; i--) {
25336 var a = node.attributes[i];
25339 if (a.name.toLowerCase().substr(0,2)=='on') {
25340 node.removeAttribute(a.name);
25343 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25344 node.removeAttribute(a.name);
25347 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25348 cleanAttr(a.name,a.value); // fixme..
25351 if (a.name == 'style') {
25352 cleanStyle(a.name,a.value);
25355 /// clean up MS crap..
25356 // tecnically this should be a list of valid class'es..
25359 if (a.name == 'class') {
25360 if (a.value.match(/^Mso/)) {
25361 node.removeAttribute('class');
25364 if (a.value.match(/^body$/)) {
25365 node.removeAttribute('class');
25376 this.cleanUpChildren(node);
25382 * Clean up MS wordisms...
25384 cleanWord : function(node)
25387 this.cleanWord(this.doc.body);
25392 node.nodeName == 'SPAN' &&
25393 !node.hasAttributes() &&
25394 node.childNodes.length == 1 &&
25395 node.firstChild.nodeName == "#text"
25397 var textNode = node.firstChild;
25398 node.removeChild(textNode);
25399 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25400 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25402 node.parentNode.insertBefore(textNode, node);
25403 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25404 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25406 node.parentNode.removeChild(node);
25409 if (node.nodeName == "#text") {
25410 // clean up silly Windows -- stuff?
25413 if (node.nodeName == "#comment") {
25414 node.parentNode.removeChild(node);
25415 // clean up silly Windows -- stuff?
25419 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25420 node.parentNode.removeChild(node);
25423 //Roo.log(node.tagName);
25424 // remove - but keep children..
25425 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25426 //Roo.log('-- removed');
25427 while (node.childNodes.length) {
25428 var cn = node.childNodes[0];
25429 node.removeChild(cn);
25430 node.parentNode.insertBefore(cn, node);
25431 // move node to parent - and clean it..
25432 this.cleanWord(cn);
25434 node.parentNode.removeChild(node);
25435 /// no need to iterate chidlren = it's got none..
25436 //this.iterateChildren(node, this.cleanWord);
25440 if (node.className.length) {
25442 var cn = node.className.split(/\W+/);
25444 Roo.each(cn, function(cls) {
25445 if (cls.match(/Mso[a-zA-Z]+/)) {
25450 node.className = cna.length ? cna.join(' ') : '';
25452 node.removeAttribute("class");
25456 if (node.hasAttribute("lang")) {
25457 node.removeAttribute("lang");
25460 if (node.hasAttribute("style")) {
25462 var styles = node.getAttribute("style").split(";");
25464 Roo.each(styles, function(s) {
25465 if (!s.match(/:/)) {
25468 var kv = s.split(":");
25469 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25472 // what ever is left... we allow.
25475 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25476 if (!nstyle.length) {
25477 node.removeAttribute('style');
25480 this.iterateChildren(node, this.cleanWord);
25486 * iterateChildren of a Node, calling fn each time, using this as the scole..
25487 * @param {DomNode} node node to iterate children of.
25488 * @param {Function} fn method of this class to call on each item.
25490 iterateChildren : function(node, fn)
25492 if (!node.childNodes.length) {
25495 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25496 fn.call(this, node.childNodes[i])
25502 * cleanTableWidths.
25504 * Quite often pasting from word etc.. results in tables with column and widths.
25505 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25508 cleanTableWidths : function(node)
25513 this.cleanTableWidths(this.doc.body);
25518 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25521 Roo.log(node.tagName);
25522 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25523 this.iterateChildren(node, this.cleanTableWidths);
25526 if (node.hasAttribute('width')) {
25527 node.removeAttribute('width');
25531 if (node.hasAttribute("style")) {
25534 var styles = node.getAttribute("style").split(";");
25536 Roo.each(styles, function(s) {
25537 if (!s.match(/:/)) {
25540 var kv = s.split(":");
25541 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25544 // what ever is left... we allow.
25547 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25548 if (!nstyle.length) {
25549 node.removeAttribute('style');
25553 this.iterateChildren(node, this.cleanTableWidths);
25561 domToHTML : function(currentElement, depth, nopadtext) {
25563 depth = depth || 0;
25564 nopadtext = nopadtext || false;
25566 if (!currentElement) {
25567 return this.domToHTML(this.doc.body);
25570 //Roo.log(currentElement);
25572 var allText = false;
25573 var nodeName = currentElement.nodeName;
25574 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25576 if (nodeName == '#text') {
25578 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25583 if (nodeName != 'BODY') {
25586 // Prints the node tagName, such as <A>, <IMG>, etc
25589 for(i = 0; i < currentElement.attributes.length;i++) {
25591 var aname = currentElement.attributes.item(i).name;
25592 if (!currentElement.attributes.item(i).value.length) {
25595 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25598 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25607 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25610 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25615 // Traverse the tree
25617 var currentElementChild = currentElement.childNodes.item(i);
25618 var allText = true;
25619 var innerHTML = '';
25621 while (currentElementChild) {
25622 // Formatting code (indent the tree so it looks nice on the screen)
25623 var nopad = nopadtext;
25624 if (lastnode == 'SPAN') {
25628 if (currentElementChild.nodeName == '#text') {
25629 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25630 toadd = nopadtext ? toadd : toadd.trim();
25631 if (!nopad && toadd.length > 80) {
25632 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25634 innerHTML += toadd;
25637 currentElementChild = currentElement.childNodes.item(i);
25643 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25645 // Recursively traverse the tree structure of the child node
25646 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25647 lastnode = currentElementChild.nodeName;
25649 currentElementChild=currentElement.childNodes.item(i);
25655 // The remaining code is mostly for formatting the tree
25656 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25661 ret+= "</"+tagName+">";
25667 applyBlacklists : function()
25669 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25670 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25674 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25675 if (b.indexOf(tag) > -1) {
25678 this.white.push(tag);
25682 Roo.each(w, function(tag) {
25683 if (b.indexOf(tag) > -1) {
25686 if (this.white.indexOf(tag) > -1) {
25689 this.white.push(tag);
25694 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25695 if (w.indexOf(tag) > -1) {
25698 this.black.push(tag);
25702 Roo.each(b, function(tag) {
25703 if (w.indexOf(tag) > -1) {
25706 if (this.black.indexOf(tag) > -1) {
25709 this.black.push(tag);
25714 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25715 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25719 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25720 if (b.indexOf(tag) > -1) {
25723 this.cwhite.push(tag);
25727 Roo.each(w, function(tag) {
25728 if (b.indexOf(tag) > -1) {
25731 if (this.cwhite.indexOf(tag) > -1) {
25734 this.cwhite.push(tag);
25739 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25740 if (w.indexOf(tag) > -1) {
25743 this.cblack.push(tag);
25747 Roo.each(b, function(tag) {
25748 if (w.indexOf(tag) > -1) {
25751 if (this.cblack.indexOf(tag) > -1) {
25754 this.cblack.push(tag);
25759 setStylesheets : function(stylesheets)
25761 if(typeof(stylesheets) == 'string'){
25762 Roo.get(this.iframe.contentDocument.head).createChild({
25764 rel : 'stylesheet',
25773 Roo.each(stylesheets, function(s) {
25778 Roo.get(_this.iframe.contentDocument.head).createChild({
25780 rel : 'stylesheet',
25789 removeStylesheets : function()
25793 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25798 setStyle : function(style)
25800 Roo.get(this.iframe.contentDocument.head).createChild({
25809 // hide stuff that is not compatible
25823 * @event specialkey
25827 * @cfg {String} fieldClass @hide
25830 * @cfg {String} focusClass @hide
25833 * @cfg {String} autoCreate @hide
25836 * @cfg {String} inputType @hide
25839 * @cfg {String} invalidClass @hide
25842 * @cfg {String} invalidText @hide
25845 * @cfg {String} msgFx @hide
25848 * @cfg {String} validateOnBlur @hide
25852 Roo.HtmlEditorCore.white = [
25853 'area', 'br', 'img', 'input', 'hr', 'wbr',
25855 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25856 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25857 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25858 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25859 'table', 'ul', 'xmp',
25861 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25864 'dir', 'menu', 'ol', 'ul', 'dl',
25870 Roo.HtmlEditorCore.black = [
25871 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25873 'base', 'basefont', 'bgsound', 'blink', 'body',
25874 'frame', 'frameset', 'head', 'html', 'ilayer',
25875 'iframe', 'layer', 'link', 'meta', 'object',
25876 'script', 'style' ,'title', 'xml' // clean later..
25878 Roo.HtmlEditorCore.clean = [
25879 'script', 'style', 'title', 'xml'
25881 Roo.HtmlEditorCore.remove = [
25886 Roo.HtmlEditorCore.ablack = [
25890 Roo.HtmlEditorCore.aclean = [
25891 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25895 Roo.HtmlEditorCore.pwhite= [
25896 'http', 'https', 'mailto'
25899 // white listed style attributes.
25900 Roo.HtmlEditorCore.cwhite= [
25901 // 'text-align', /// default is to allow most things..
25907 // black listed style attributes.
25908 Roo.HtmlEditorCore.cblack= [
25909 // 'font-size' -- this can be set by the project
25913 Roo.HtmlEditorCore.swapCodes =[
25914 [ 8211, "–" ],
25915 [ 8212, "—" ],
25932 * @class Roo.bootstrap.HtmlEditor
25933 * @extends Roo.bootstrap.TextArea
25934 * Bootstrap HtmlEditor class
25937 * Create a new HtmlEditor
25938 * @param {Object} config The config object
25941 Roo.bootstrap.HtmlEditor = function(config){
25942 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25943 if (!this.toolbars) {
25944 this.toolbars = [];
25947 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25950 * @event initialize
25951 * Fires when the editor is fully initialized (including the iframe)
25952 * @param {HtmlEditor} this
25957 * Fires when the editor is first receives the focus. Any insertion must wait
25958 * until after this event.
25959 * @param {HtmlEditor} this
25963 * @event beforesync
25964 * Fires before the textarea is updated with content from the editor iframe. Return false
25965 * to cancel the sync.
25966 * @param {HtmlEditor} this
25967 * @param {String} html
25971 * @event beforepush
25972 * Fires before the iframe editor is updated with content from the textarea. Return false
25973 * to cancel the push.
25974 * @param {HtmlEditor} this
25975 * @param {String} html
25980 * Fires when the textarea is updated with content from the editor iframe.
25981 * @param {HtmlEditor} this
25982 * @param {String} html
25987 * Fires when the iframe editor is updated with content from the textarea.
25988 * @param {HtmlEditor} this
25989 * @param {String} html
25993 * @event editmodechange
25994 * Fires when the editor switches edit modes
25995 * @param {HtmlEditor} this
25996 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25998 editmodechange: true,
26000 * @event editorevent
26001 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26002 * @param {HtmlEditor} this
26006 * @event firstfocus
26007 * Fires when on first focus - needed by toolbars..
26008 * @param {HtmlEditor} this
26013 * Auto save the htmlEditor value as a file into Events
26014 * @param {HtmlEditor} this
26018 * @event savedpreview
26019 * preview the saved version of htmlEditor
26020 * @param {HtmlEditor} this
26027 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26031 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26036 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26041 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26046 * @cfg {Number} height (in pixels)
26050 * @cfg {Number} width (in pixels)
26055 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26058 stylesheets: false,
26063 // private properties
26064 validationEvent : false,
26066 initialized : false,
26069 onFocus : Roo.emptyFn,
26071 hideMode:'offsets',
26073 tbContainer : false,
26077 toolbarContainer :function() {
26078 return this.wrap.select('.x-html-editor-tb',true).first();
26082 * Protected method that will not generally be called directly. It
26083 * is called when the editor creates its toolbar. Override this method if you need to
26084 * add custom toolbar buttons.
26085 * @param {HtmlEditor} editor
26087 createToolbar : function(){
26088 Roo.log('renewing');
26089 Roo.log("create toolbars");
26091 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26092 this.toolbars[0].render(this.toolbarContainer());
26096 // if (!editor.toolbars || !editor.toolbars.length) {
26097 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26100 // for (var i =0 ; i < editor.toolbars.length;i++) {
26101 // editor.toolbars[i] = Roo.factory(
26102 // typeof(editor.toolbars[i]) == 'string' ?
26103 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26104 // Roo.bootstrap.HtmlEditor);
26105 // editor.toolbars[i].init(editor);
26111 onRender : function(ct, position)
26113 // Roo.log("Call onRender: " + this.xtype);
26115 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26117 this.wrap = this.inputEl().wrap({
26118 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26121 this.editorcore.onRender(ct, position);
26123 if (this.resizable) {
26124 this.resizeEl = new Roo.Resizable(this.wrap, {
26128 minHeight : this.height,
26129 height: this.height,
26130 handles : this.resizable,
26133 resize : function(r, w, h) {
26134 _t.onResize(w,h); // -something
26140 this.createToolbar(this);
26143 if(!this.width && this.resizable){
26144 this.setSize(this.wrap.getSize());
26146 if (this.resizeEl) {
26147 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26148 // should trigger onReize..
26154 onResize : function(w, h)
26156 Roo.log('resize: ' +w + ',' + h );
26157 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26161 if(this.inputEl() ){
26162 if(typeof w == 'number'){
26163 var aw = w - this.wrap.getFrameWidth('lr');
26164 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26167 if(typeof h == 'number'){
26168 var tbh = -11; // fixme it needs to tool bar size!
26169 for (var i =0; i < this.toolbars.length;i++) {
26170 // fixme - ask toolbars for heights?
26171 tbh += this.toolbars[i].el.getHeight();
26172 //if (this.toolbars[i].footer) {
26173 // tbh += this.toolbars[i].footer.el.getHeight();
26181 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26182 ah -= 5; // knock a few pixes off for look..
26183 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26187 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26188 this.editorcore.onResize(ew,eh);
26193 * Toggles the editor between standard and source edit mode.
26194 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26196 toggleSourceEdit : function(sourceEditMode)
26198 this.editorcore.toggleSourceEdit(sourceEditMode);
26200 if(this.editorcore.sourceEditMode){
26201 Roo.log('editor - showing textarea');
26204 // Roo.log(this.syncValue());
26206 this.inputEl().removeClass(['hide', 'x-hidden']);
26207 this.inputEl().dom.removeAttribute('tabIndex');
26208 this.inputEl().focus();
26210 Roo.log('editor - hiding textarea');
26212 // Roo.log(this.pushValue());
26215 this.inputEl().addClass(['hide', 'x-hidden']);
26216 this.inputEl().dom.setAttribute('tabIndex', -1);
26217 //this.deferFocus();
26220 if(this.resizable){
26221 this.setSize(this.wrap.getSize());
26224 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26227 // private (for BoxComponent)
26228 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26230 // private (for BoxComponent)
26231 getResizeEl : function(){
26235 // private (for BoxComponent)
26236 getPositionEl : function(){
26241 initEvents : function(){
26242 this.originalValue = this.getValue();
26246 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26249 // markInvalid : Roo.emptyFn,
26251 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26254 // clearInvalid : Roo.emptyFn,
26256 setValue : function(v){
26257 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26258 this.editorcore.pushValue();
26263 deferFocus : function(){
26264 this.focus.defer(10, this);
26268 focus : function(){
26269 this.editorcore.focus();
26275 onDestroy : function(){
26281 for (var i =0; i < this.toolbars.length;i++) {
26282 // fixme - ask toolbars for heights?
26283 this.toolbars[i].onDestroy();
26286 this.wrap.dom.innerHTML = '';
26287 this.wrap.remove();
26292 onFirstFocus : function(){
26293 //Roo.log("onFirstFocus");
26294 this.editorcore.onFirstFocus();
26295 for (var i =0; i < this.toolbars.length;i++) {
26296 this.toolbars[i].onFirstFocus();
26302 syncValue : function()
26304 this.editorcore.syncValue();
26307 pushValue : function()
26309 this.editorcore.pushValue();
26313 // hide stuff that is not compatible
26327 * @event specialkey
26331 * @cfg {String} fieldClass @hide
26334 * @cfg {String} focusClass @hide
26337 * @cfg {String} autoCreate @hide
26340 * @cfg {String} inputType @hide
26344 * @cfg {String} invalidText @hide
26347 * @cfg {String} msgFx @hide
26350 * @cfg {String} validateOnBlur @hide
26359 Roo.namespace('Roo.bootstrap.htmleditor');
26361 * @class Roo.bootstrap.HtmlEditorToolbar1
26367 new Roo.bootstrap.HtmlEditor({
26370 new Roo.bootstrap.HtmlEditorToolbar1({
26371 disable : { fonts: 1 , format: 1, ..., ... , ...],
26377 * @cfg {Object} disable List of elements to disable..
26378 * @cfg {Array} btns List of additional buttons.
26382 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26385 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26388 Roo.apply(this, config);
26390 // default disabled, based on 'good practice'..
26391 this.disable = this.disable || {};
26392 Roo.applyIf(this.disable, {
26395 specialElements : true
26397 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26399 this.editor = config.editor;
26400 this.editorcore = config.editor.editorcore;
26402 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26404 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26405 // dont call parent... till later.
26407 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26412 editorcore : false,
26417 "h1","h2","h3","h4","h5","h6",
26419 "abbr", "acronym", "address", "cite", "samp", "var",
26423 onRender : function(ct, position)
26425 // Roo.log("Call onRender: " + this.xtype);
26427 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26429 this.el.dom.style.marginBottom = '0';
26431 var editorcore = this.editorcore;
26432 var editor= this.editor;
26435 var btn = function(id,cmd , toggle, handler, html){
26437 var event = toggle ? 'toggle' : 'click';
26442 xns: Roo.bootstrap,
26446 enableToggle:toggle !== false,
26448 pressed : toggle ? false : null,
26451 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26452 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26458 // var cb_box = function...
26463 xns: Roo.bootstrap,
26468 xns: Roo.bootstrap,
26472 Roo.each(this.formats, function(f) {
26473 style.menu.items.push({
26475 xns: Roo.bootstrap,
26476 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26481 editorcore.insertTag(this.tagname);
26488 children.push(style);
26490 btn('bold',false,true);
26491 btn('italic',false,true);
26492 btn('align-left', 'justifyleft',true);
26493 btn('align-center', 'justifycenter',true);
26494 btn('align-right' , 'justifyright',true);
26495 btn('link', false, false, function(btn) {
26496 //Roo.log("create link?");
26497 var url = prompt(this.createLinkText, this.defaultLinkValue);
26498 if(url && url != 'http:/'+'/'){
26499 this.editorcore.relayCmd('createlink', url);
26502 btn('list','insertunorderedlist',true);
26503 btn('pencil', false,true, function(btn){
26505 this.toggleSourceEdit(btn.pressed);
26508 if (this.editor.btns.length > 0) {
26509 for (var i = 0; i<this.editor.btns.length; i++) {
26510 children.push(this.editor.btns[i]);
26518 xns: Roo.bootstrap,
26523 xns: Roo.bootstrap,
26528 cog.menu.items.push({
26530 xns: Roo.bootstrap,
26531 html : Clean styles,
26536 editorcore.insertTag(this.tagname);
26545 this.xtype = 'NavSimplebar';
26547 for(var i=0;i< children.length;i++) {
26549 this.buttons.add(this.addxtypeChild(children[i]));
26553 editor.on('editorevent', this.updateToolbar, this);
26555 onBtnClick : function(id)
26557 this.editorcore.relayCmd(id);
26558 this.editorcore.focus();
26562 * Protected method that will not generally be called directly. It triggers
26563 * a toolbar update by reading the markup state of the current selection in the editor.
26565 updateToolbar: function(){
26567 if(!this.editorcore.activated){
26568 this.editor.onFirstFocus(); // is this neeed?
26572 var btns = this.buttons;
26573 var doc = this.editorcore.doc;
26574 btns.get('bold').setActive(doc.queryCommandState('bold'));
26575 btns.get('italic').setActive(doc.queryCommandState('italic'));
26576 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26578 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26579 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26580 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26582 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26583 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26586 var ans = this.editorcore.getAllAncestors();
26587 if (this.formatCombo) {
26590 var store = this.formatCombo.store;
26591 this.formatCombo.setValue("");
26592 for (var i =0; i < ans.length;i++) {
26593 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26595 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26603 // hides menus... - so this cant be on a menu...
26604 Roo.bootstrap.MenuMgr.hideAll();
26606 Roo.bootstrap.MenuMgr.hideAll();
26607 //this.editorsyncValue();
26609 onFirstFocus: function() {
26610 this.buttons.each(function(item){
26614 toggleSourceEdit : function(sourceEditMode){
26617 if(sourceEditMode){
26618 Roo.log("disabling buttons");
26619 this.buttons.each( function(item){
26620 if(item.cmd != 'pencil'){
26626 Roo.log("enabling buttons");
26627 if(this.editorcore.initialized){
26628 this.buttons.each( function(item){
26634 Roo.log("calling toggole on editor");
26635 // tell the editor that it's been pressed..
26636 this.editor.toggleSourceEdit(sourceEditMode);
26650 * @class Roo.bootstrap.Markdown
26651 * @extends Roo.bootstrap.TextArea
26652 * Bootstrap Showdown editable area
26653 * @cfg {string} content
26656 * Create a new Showdown
26659 Roo.bootstrap.Markdown = function(config){
26660 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26664 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26668 initEvents : function()
26671 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26672 this.markdownEl = this.el.createChild({
26673 cls : 'roo-markdown-area'
26675 this.inputEl().addClass('d-none');
26676 if (this.getValue() == '') {
26677 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26680 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26682 this.markdownEl.on('click', this.toggleTextEdit, this);
26683 this.on('blur', this.toggleTextEdit, this);
26684 this.on('specialkey', this.resizeTextArea, this);
26687 toggleTextEdit : function()
26689 var sh = this.markdownEl.getHeight();
26690 this.inputEl().addClass('d-none');
26691 this.markdownEl.addClass('d-none');
26692 if (!this.editing) {
26694 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26695 this.inputEl().removeClass('d-none');
26696 this.inputEl().focus();
26697 this.editing = true;
26700 // show showdown...
26701 this.updateMarkdown();
26702 this.markdownEl.removeClass('d-none');
26703 this.editing = false;
26706 updateMarkdown : function()
26708 if (this.getValue() == '') {
26709 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26713 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26716 resizeTextArea: function () {
26719 Roo.log([sh, this.getValue().split("\n").length * 30]);
26720 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26722 setValue : function(val)
26724 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26725 if (!this.editing) {
26726 this.updateMarkdown();
26732 if (!this.editing) {
26733 this.toggleTextEdit();
26741 * @class Roo.bootstrap.Table.AbstractSelectionModel
26742 * @extends Roo.util.Observable
26743 * Abstract base class for grid SelectionModels. It provides the interface that should be
26744 * implemented by descendant classes. This class should not be directly instantiated.
26747 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26748 this.locked = false;
26749 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26753 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26754 /** @ignore Called by the grid automatically. Do not call directly. */
26755 init : function(grid){
26761 * Locks the selections.
26764 this.locked = true;
26768 * Unlocks the selections.
26770 unlock : function(){
26771 this.locked = false;
26775 * Returns true if the selections are locked.
26776 * @return {Boolean}
26778 isLocked : function(){
26779 return this.locked;
26783 initEvents : function ()
26789 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26790 * @class Roo.bootstrap.Table.RowSelectionModel
26791 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26792 * It supports multiple selections and keyboard selection/navigation.
26794 * @param {Object} config
26797 Roo.bootstrap.Table.RowSelectionModel = function(config){
26798 Roo.apply(this, config);
26799 this.selections = new Roo.util.MixedCollection(false, function(o){
26804 this.lastActive = false;
26808 * @event selectionchange
26809 * Fires when the selection changes
26810 * @param {SelectionModel} this
26812 "selectionchange" : true,
26814 * @event afterselectionchange
26815 * Fires after the selection changes (eg. by key press or clicking)
26816 * @param {SelectionModel} this
26818 "afterselectionchange" : true,
26820 * @event beforerowselect
26821 * Fires when a row is selected being selected, return false to cancel.
26822 * @param {SelectionModel} this
26823 * @param {Number} rowIndex The selected index
26824 * @param {Boolean} keepExisting False if other selections will be cleared
26826 "beforerowselect" : true,
26829 * Fires when a row is selected.
26830 * @param {SelectionModel} this
26831 * @param {Number} rowIndex The selected index
26832 * @param {Roo.data.Record} r The record
26834 "rowselect" : true,
26836 * @event rowdeselect
26837 * Fires when a row is deselected.
26838 * @param {SelectionModel} this
26839 * @param {Number} rowIndex The selected index
26841 "rowdeselect" : true
26843 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26844 this.locked = false;
26847 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26849 * @cfg {Boolean} singleSelect
26850 * True to allow selection of only one row at a time (defaults to false)
26852 singleSelect : false,
26855 initEvents : function()
26858 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26859 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26860 //}else{ // allow click to work like normal
26861 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26863 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26864 this.grid.on("rowclick", this.handleMouseDown, this);
26866 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26867 "up" : function(e){
26869 this.selectPrevious(e.shiftKey);
26870 }else if(this.last !== false && this.lastActive !== false){
26871 var last = this.last;
26872 this.selectRange(this.last, this.lastActive-1);
26873 this.grid.getView().focusRow(this.lastActive);
26874 if(last !== false){
26878 this.selectFirstRow();
26880 this.fireEvent("afterselectionchange", this);
26882 "down" : function(e){
26884 this.selectNext(e.shiftKey);
26885 }else if(this.last !== false && this.lastActive !== false){
26886 var last = this.last;
26887 this.selectRange(this.last, this.lastActive+1);
26888 this.grid.getView().focusRow(this.lastActive);
26889 if(last !== false){
26893 this.selectFirstRow();
26895 this.fireEvent("afterselectionchange", this);
26899 this.grid.store.on('load', function(){
26900 this.selections.clear();
26903 var view = this.grid.view;
26904 view.on("refresh", this.onRefresh, this);
26905 view.on("rowupdated", this.onRowUpdated, this);
26906 view.on("rowremoved", this.onRemove, this);
26911 onRefresh : function()
26913 var ds = this.grid.store, i, v = this.grid.view;
26914 var s = this.selections;
26915 s.each(function(r){
26916 if((i = ds.indexOfId(r.id)) != -1){
26925 onRemove : function(v, index, r){
26926 this.selections.remove(r);
26930 onRowUpdated : function(v, index, r){
26931 if(this.isSelected(r)){
26932 v.onRowSelect(index);
26938 * @param {Array} records The records to select
26939 * @param {Boolean} keepExisting (optional) True to keep existing selections
26941 selectRecords : function(records, keepExisting)
26944 this.clearSelections();
26946 var ds = this.grid.store;
26947 for(var i = 0, len = records.length; i < len; i++){
26948 this.selectRow(ds.indexOf(records[i]), true);
26953 * Gets the number of selected rows.
26956 getCount : function(){
26957 return this.selections.length;
26961 * Selects the first row in the grid.
26963 selectFirstRow : function(){
26968 * Select the last row.
26969 * @param {Boolean} keepExisting (optional) True to keep existing selections
26971 selectLastRow : function(keepExisting){
26972 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26973 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26977 * Selects the row immediately following the last selected row.
26978 * @param {Boolean} keepExisting (optional) True to keep existing selections
26980 selectNext : function(keepExisting)
26982 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26983 this.selectRow(this.last+1, keepExisting);
26984 this.grid.getView().focusRow(this.last);
26989 * Selects the row that precedes the last selected row.
26990 * @param {Boolean} keepExisting (optional) True to keep existing selections
26992 selectPrevious : function(keepExisting){
26994 this.selectRow(this.last-1, keepExisting);
26995 this.grid.getView().focusRow(this.last);
27000 * Returns the selected records
27001 * @return {Array} Array of selected records
27003 getSelections : function(){
27004 return [].concat(this.selections.items);
27008 * Returns the first selected record.
27011 getSelected : function(){
27012 return this.selections.itemAt(0);
27017 * Clears all selections.
27019 clearSelections : function(fast)
27025 var ds = this.grid.store;
27026 var s = this.selections;
27027 s.each(function(r){
27028 this.deselectRow(ds.indexOfId(r.id));
27032 this.selections.clear();
27039 * Selects all rows.
27041 selectAll : function(){
27045 this.selections.clear();
27046 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27047 this.selectRow(i, true);
27052 * Returns True if there is a selection.
27053 * @return {Boolean}
27055 hasSelection : function(){
27056 return this.selections.length > 0;
27060 * Returns True if the specified row is selected.
27061 * @param {Number/Record} record The record or index of the record to check
27062 * @return {Boolean}
27064 isSelected : function(index){
27065 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27066 return (r && this.selections.key(r.id) ? true : false);
27070 * Returns True if the specified record id is selected.
27071 * @param {String} id The id of record to check
27072 * @return {Boolean}
27074 isIdSelected : function(id){
27075 return (this.selections.key(id) ? true : false);
27080 handleMouseDBClick : function(e, t){
27084 handleMouseDown : function(e, t)
27086 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27087 if(this.isLocked() || rowIndex < 0 ){
27090 if(e.shiftKey && this.last !== false){
27091 var last = this.last;
27092 this.selectRange(last, rowIndex, e.ctrlKey);
27093 this.last = last; // reset the last
27097 var isSelected = this.isSelected(rowIndex);
27098 //Roo.log("select row:" + rowIndex);
27100 this.deselectRow(rowIndex);
27102 this.selectRow(rowIndex, true);
27106 if(e.button !== 0 && isSelected){
27107 alert('rowIndex 2: ' + rowIndex);
27108 view.focusRow(rowIndex);
27109 }else if(e.ctrlKey && isSelected){
27110 this.deselectRow(rowIndex);
27111 }else if(!isSelected){
27112 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27113 view.focusRow(rowIndex);
27117 this.fireEvent("afterselectionchange", this);
27120 handleDragableRowClick : function(grid, rowIndex, e)
27122 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27123 this.selectRow(rowIndex, false);
27124 grid.view.focusRow(rowIndex);
27125 this.fireEvent("afterselectionchange", this);
27130 * Selects multiple rows.
27131 * @param {Array} rows Array of the indexes of the row to select
27132 * @param {Boolean} keepExisting (optional) True to keep existing selections
27134 selectRows : function(rows, keepExisting){
27136 this.clearSelections();
27138 for(var i = 0, len = rows.length; i < len; i++){
27139 this.selectRow(rows[i], true);
27144 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27145 * @param {Number} startRow The index of the first row in the range
27146 * @param {Number} endRow The index of the last row in the range
27147 * @param {Boolean} keepExisting (optional) True to retain existing selections
27149 selectRange : function(startRow, endRow, keepExisting){
27154 this.clearSelections();
27156 if(startRow <= endRow){
27157 for(var i = startRow; i <= endRow; i++){
27158 this.selectRow(i, true);
27161 for(var i = startRow; i >= endRow; i--){
27162 this.selectRow(i, true);
27168 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27169 * @param {Number} startRow The index of the first row in the range
27170 * @param {Number} endRow The index of the last row in the range
27172 deselectRange : function(startRow, endRow, preventViewNotify){
27176 for(var i = startRow; i <= endRow; i++){
27177 this.deselectRow(i, preventViewNotify);
27183 * @param {Number} row The index of the row to select
27184 * @param {Boolean} keepExisting (optional) True to keep existing selections
27186 selectRow : function(index, keepExisting, preventViewNotify)
27188 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27191 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27192 if(!keepExisting || this.singleSelect){
27193 this.clearSelections();
27196 var r = this.grid.store.getAt(index);
27197 //console.log('selectRow - record id :' + r.id);
27199 this.selections.add(r);
27200 this.last = this.lastActive = index;
27201 if(!preventViewNotify){
27202 var proxy = new Roo.Element(
27203 this.grid.getRowDom(index)
27205 proxy.addClass('bg-info info');
27207 this.fireEvent("rowselect", this, index, r);
27208 this.fireEvent("selectionchange", this);
27214 * @param {Number} row The index of the row to deselect
27216 deselectRow : function(index, preventViewNotify)
27221 if(this.last == index){
27224 if(this.lastActive == index){
27225 this.lastActive = false;
27228 var r = this.grid.store.getAt(index);
27233 this.selections.remove(r);
27234 //.console.log('deselectRow - record id :' + r.id);
27235 if(!preventViewNotify){
27237 var proxy = new Roo.Element(
27238 this.grid.getRowDom(index)
27240 proxy.removeClass('bg-info info');
27242 this.fireEvent("rowdeselect", this, index);
27243 this.fireEvent("selectionchange", this);
27247 restoreLast : function(){
27249 this.last = this._last;
27254 acceptsNav : function(row, col, cm){
27255 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27259 onEditorKey : function(field, e){
27260 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27265 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27267 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27269 }else if(k == e.ENTER && !e.ctrlKey){
27273 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27275 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27277 }else if(k == e.ESC){
27281 g.startEditing(newCell[0], newCell[1]);
27287 * Ext JS Library 1.1.1
27288 * Copyright(c) 2006-2007, Ext JS, LLC.
27290 * Originally Released Under LGPL - original licence link has changed is not relivant.
27293 * <script type="text/javascript">
27297 * @class Roo.bootstrap.PagingToolbar
27298 * @extends Roo.bootstrap.NavSimplebar
27299 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27301 * Create a new PagingToolbar
27302 * @param {Object} config The config object
27303 * @param {Roo.data.Store} store
27305 Roo.bootstrap.PagingToolbar = function(config)
27307 // old args format still supported... - xtype is prefered..
27308 // created from xtype...
27310 this.ds = config.dataSource;
27312 if (config.store && !this.ds) {
27313 this.store= Roo.factory(config.store, Roo.data);
27314 this.ds = this.store;
27315 this.ds.xmodule = this.xmodule || false;
27318 this.toolbarItems = [];
27319 if (config.items) {
27320 this.toolbarItems = config.items;
27323 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27328 this.bind(this.ds);
27331 if (Roo.bootstrap.version == 4) {
27332 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27334 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27339 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27341 * @cfg {Roo.data.Store} dataSource
27342 * The underlying data store providing the paged data
27345 * @cfg {String/HTMLElement/Element} container
27346 * container The id or element that will contain the toolbar
27349 * @cfg {Boolean} displayInfo
27350 * True to display the displayMsg (defaults to false)
27353 * @cfg {Number} pageSize
27354 * The number of records to display per page (defaults to 20)
27358 * @cfg {String} displayMsg
27359 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27361 displayMsg : 'Displaying {0} - {1} of {2}',
27363 * @cfg {String} emptyMsg
27364 * The message to display when no records are found (defaults to "No data to display")
27366 emptyMsg : 'No data to display',
27368 * Customizable piece of the default paging text (defaults to "Page")
27371 beforePageText : "Page",
27373 * Customizable piece of the default paging text (defaults to "of %0")
27376 afterPageText : "of {0}",
27378 * Customizable piece of the default paging text (defaults to "First Page")
27381 firstText : "First Page",
27383 * Customizable piece of the default paging text (defaults to "Previous Page")
27386 prevText : "Previous Page",
27388 * Customizable piece of the default paging text (defaults to "Next Page")
27391 nextText : "Next Page",
27393 * Customizable piece of the default paging text (defaults to "Last Page")
27396 lastText : "Last Page",
27398 * Customizable piece of the default paging text (defaults to "Refresh")
27401 refreshText : "Refresh",
27405 onRender : function(ct, position)
27407 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27408 this.navgroup.parentId = this.id;
27409 this.navgroup.onRender(this.el, null);
27410 // add the buttons to the navgroup
27412 if(this.displayInfo){
27413 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27414 this.displayEl = this.el.select('.x-paging-info', true).first();
27415 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27416 // this.displayEl = navel.el.select('span',true).first();
27422 Roo.each(_this.buttons, function(e){ // this might need to use render????
27423 Roo.factory(e).render(_this.el);
27427 Roo.each(_this.toolbarItems, function(e) {
27428 _this.navgroup.addItem(e);
27432 this.first = this.navgroup.addItem({
27433 tooltip: this.firstText,
27434 cls: "prev btn-outline-secondary",
27435 html : ' <i class="fa fa-step-backward"></i>',
27437 preventDefault: true,
27438 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27441 this.prev = this.navgroup.addItem({
27442 tooltip: this.prevText,
27443 cls: "prev btn-outline-secondary",
27444 html : ' <i class="fa fa-backward"></i>',
27446 preventDefault: true,
27447 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27449 //this.addSeparator();
27452 var field = this.navgroup.addItem( {
27454 cls : 'x-paging-position btn-outline-secondary',
27456 html : this.beforePageText +
27457 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27458 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27461 this.field = field.el.select('input', true).first();
27462 this.field.on("keydown", this.onPagingKeydown, this);
27463 this.field.on("focus", function(){this.dom.select();});
27466 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27467 //this.field.setHeight(18);
27468 //this.addSeparator();
27469 this.next = this.navgroup.addItem({
27470 tooltip: this.nextText,
27471 cls: "next btn-outline-secondary",
27472 html : ' <i class="fa fa-forward"></i>',
27474 preventDefault: true,
27475 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27477 this.last = this.navgroup.addItem({
27478 tooltip: this.lastText,
27479 html : ' <i class="fa fa-step-forward"></i>',
27480 cls: "next btn-outline-secondary",
27482 preventDefault: true,
27483 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27485 //this.addSeparator();
27486 this.loading = this.navgroup.addItem({
27487 tooltip: this.refreshText,
27488 cls: "btn-outline-secondary",
27489 html : ' <i class="fa fa-refresh"></i>',
27490 preventDefault: true,
27491 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27497 updateInfo : function(){
27498 if(this.displayEl){
27499 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27500 var msg = count == 0 ?
27504 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27506 this.displayEl.update(msg);
27511 onLoad : function(ds, r, o)
27513 this.cursor = o.params && o.params.start ? o.params.start : 0;
27515 var d = this.getPageData(),
27520 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27521 this.field.dom.value = ap;
27522 this.first.setDisabled(ap == 1);
27523 this.prev.setDisabled(ap == 1);
27524 this.next.setDisabled(ap == ps);
27525 this.last.setDisabled(ap == ps);
27526 this.loading.enable();
27531 getPageData : function(){
27532 var total = this.ds.getTotalCount();
27535 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27536 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27541 onLoadError : function(){
27542 this.loading.enable();
27546 onPagingKeydown : function(e){
27547 var k = e.getKey();
27548 var d = this.getPageData();
27550 var v = this.field.dom.value, pageNum;
27551 if(!v || isNaN(pageNum = parseInt(v, 10))){
27552 this.field.dom.value = d.activePage;
27555 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27556 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27559 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))
27561 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27562 this.field.dom.value = pageNum;
27563 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27566 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27568 var v = this.field.dom.value, pageNum;
27569 var increment = (e.shiftKey) ? 10 : 1;
27570 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27573 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27574 this.field.dom.value = d.activePage;
27577 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27579 this.field.dom.value = parseInt(v, 10) + increment;
27580 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27581 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27588 beforeLoad : function(){
27590 this.loading.disable();
27595 onClick : function(which){
27604 ds.load({params:{start: 0, limit: this.pageSize}});
27607 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27610 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27613 var total = ds.getTotalCount();
27614 var extra = total % this.pageSize;
27615 var lastStart = extra ? (total - extra) : total-this.pageSize;
27616 ds.load({params:{start: lastStart, limit: this.pageSize}});
27619 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27625 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27626 * @param {Roo.data.Store} store The data store to unbind
27628 unbind : function(ds){
27629 ds.un("beforeload", this.beforeLoad, this);
27630 ds.un("load", this.onLoad, this);
27631 ds.un("loadexception", this.onLoadError, this);
27632 ds.un("remove", this.updateInfo, this);
27633 ds.un("add", this.updateInfo, this);
27634 this.ds = undefined;
27638 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27639 * @param {Roo.data.Store} store The data store to bind
27641 bind : function(ds){
27642 ds.on("beforeload", this.beforeLoad, this);
27643 ds.on("load", this.onLoad, this);
27644 ds.on("loadexception", this.onLoadError, this);
27645 ds.on("remove", this.updateInfo, this);
27646 ds.on("add", this.updateInfo, this);
27657 * @class Roo.bootstrap.MessageBar
27658 * @extends Roo.bootstrap.Component
27659 * Bootstrap MessageBar class
27660 * @cfg {String} html contents of the MessageBar
27661 * @cfg {String} weight (info | success | warning | danger) default info
27662 * @cfg {String} beforeClass insert the bar before the given class
27663 * @cfg {Boolean} closable (true | false) default false
27664 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27667 * Create a new Element
27668 * @param {Object} config The config object
27671 Roo.bootstrap.MessageBar = function(config){
27672 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27675 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27681 beforeClass: 'bootstrap-sticky-wrap',
27683 getAutoCreate : function(){
27687 cls: 'alert alert-dismissable alert-' + this.weight,
27692 html: this.html || ''
27698 cfg.cls += ' alert-messages-fixed';
27712 onRender : function(ct, position)
27714 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27717 var cfg = Roo.apply({}, this.getAutoCreate());
27721 cfg.cls += ' ' + this.cls;
27724 cfg.style = this.style;
27726 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27728 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27731 this.el.select('>button.close').on('click', this.hide, this);
27737 if (!this.rendered) {
27743 this.fireEvent('show', this);
27749 if (!this.rendered) {
27755 this.fireEvent('hide', this);
27758 update : function()
27760 // var e = this.el.dom.firstChild;
27762 // if(this.closable){
27763 // e = e.nextSibling;
27766 // e.data = this.html || '';
27768 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27784 * @class Roo.bootstrap.Graph
27785 * @extends Roo.bootstrap.Component
27786 * Bootstrap Graph class
27790 @cfg {String} graphtype bar | vbar | pie
27791 @cfg {number} g_x coodinator | centre x (pie)
27792 @cfg {number} g_y coodinator | centre y (pie)
27793 @cfg {number} g_r radius (pie)
27794 @cfg {number} g_height height of the chart (respected by all elements in the set)
27795 @cfg {number} g_width width of the chart (respected by all elements in the set)
27796 @cfg {Object} title The title of the chart
27799 -opts (object) options for the chart
27801 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27802 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27804 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.
27805 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27807 o stretch (boolean)
27809 -opts (object) options for the pie
27812 o startAngle (number)
27813 o endAngle (number)
27817 * Create a new Input
27818 * @param {Object} config The config object
27821 Roo.bootstrap.Graph = function(config){
27822 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27828 * The img click event for the img.
27829 * @param {Roo.EventObject} e
27835 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27846 //g_colors: this.colors,
27853 getAutoCreate : function(){
27864 onRender : function(ct,position){
27867 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27869 if (typeof(Raphael) == 'undefined') {
27870 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27874 this.raphael = Raphael(this.el.dom);
27876 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27877 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27878 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27879 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27881 r.text(160, 10, "Single Series Chart").attr(txtattr);
27882 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27883 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27884 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27886 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27887 r.barchart(330, 10, 300, 220, data1);
27888 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27889 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27892 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27893 // r.barchart(30, 30, 560, 250, xdata, {
27894 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27895 // axis : "0 0 1 1",
27896 // axisxlabels : xdata
27897 // //yvalues : cols,
27900 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27902 // this.load(null,xdata,{
27903 // axis : "0 0 1 1",
27904 // axisxlabels : xdata
27909 load : function(graphtype,xdata,opts)
27911 this.raphael.clear();
27913 graphtype = this.graphtype;
27918 var r = this.raphael,
27919 fin = function () {
27920 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27922 fout = function () {
27923 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27925 pfin = function() {
27926 this.sector.stop();
27927 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27930 this.label[0].stop();
27931 this.label[0].attr({ r: 7.5 });
27932 this.label[1].attr({ "font-weight": 800 });
27935 pfout = function() {
27936 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27939 this.label[0].animate({ r: 5 }, 500, "bounce");
27940 this.label[1].attr({ "font-weight": 400 });
27946 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27949 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27952 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27953 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27955 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27962 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27967 setTitle: function(o)
27972 initEvents: function() {
27975 this.el.on('click', this.onClick, this);
27979 onClick : function(e)
27981 Roo.log('img onclick');
27982 this.fireEvent('click', this, e);
27994 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27997 * @class Roo.bootstrap.dash.NumberBox
27998 * @extends Roo.bootstrap.Component
27999 * Bootstrap NumberBox class
28000 * @cfg {String} headline Box headline
28001 * @cfg {String} content Box content
28002 * @cfg {String} icon Box icon
28003 * @cfg {String} footer Footer text
28004 * @cfg {String} fhref Footer href
28007 * Create a new NumberBox
28008 * @param {Object} config The config object
28012 Roo.bootstrap.dash.NumberBox = function(config){
28013 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28017 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28026 getAutoCreate : function(){
28030 cls : 'small-box ',
28038 cls : 'roo-headline',
28039 html : this.headline
28043 cls : 'roo-content',
28044 html : this.content
28058 cls : 'ion ' + this.icon
28067 cls : 'small-box-footer',
28068 href : this.fhref || '#',
28072 cfg.cn.push(footer);
28079 onRender : function(ct,position){
28080 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28087 setHeadline: function (value)
28089 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28092 setFooter: function (value, href)
28094 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28097 this.el.select('a.small-box-footer',true).first().attr('href', href);
28102 setContent: function (value)
28104 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28107 initEvents: function()
28121 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28124 * @class Roo.bootstrap.dash.TabBox
28125 * @extends Roo.bootstrap.Component
28126 * Bootstrap TabBox class
28127 * @cfg {String} title Title of the TabBox
28128 * @cfg {String} icon Icon of the TabBox
28129 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28130 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28133 * Create a new TabBox
28134 * @param {Object} config The config object
28138 Roo.bootstrap.dash.TabBox = function(config){
28139 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28144 * When a pane is added
28145 * @param {Roo.bootstrap.dash.TabPane} pane
28149 * @event activatepane
28150 * When a pane is activated
28151 * @param {Roo.bootstrap.dash.TabPane} pane
28153 "activatepane" : true
28161 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28166 tabScrollable : false,
28168 getChildContainer : function()
28170 return this.el.select('.tab-content', true).first();
28173 getAutoCreate : function(){
28177 cls: 'pull-left header',
28185 cls: 'fa ' + this.icon
28191 cls: 'nav nav-tabs pull-right',
28197 if(this.tabScrollable){
28204 cls: 'nav nav-tabs pull-right',
28215 cls: 'nav-tabs-custom',
28220 cls: 'tab-content no-padding',
28228 initEvents : function()
28230 //Roo.log('add add pane handler');
28231 this.on('addpane', this.onAddPane, this);
28234 * Updates the box title
28235 * @param {String} html to set the title to.
28237 setTitle : function(value)
28239 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28241 onAddPane : function(pane)
28243 this.panes.push(pane);
28244 //Roo.log('addpane');
28246 // tabs are rendere left to right..
28247 if(!this.showtabs){
28251 var ctr = this.el.select('.nav-tabs', true).first();
28254 var existing = ctr.select('.nav-tab',true);
28255 var qty = existing.getCount();;
28258 var tab = ctr.createChild({
28260 cls : 'nav-tab' + (qty ? '' : ' active'),
28268 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28271 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28273 pane.el.addClass('active');
28278 onTabClick : function(ev,un,ob,pane)
28280 //Roo.log('tab - prev default');
28281 ev.preventDefault();
28284 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28285 pane.tab.addClass('active');
28286 //Roo.log(pane.title);
28287 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28288 // technically we should have a deactivate event.. but maybe add later.
28289 // and it should not de-activate the selected tab...
28290 this.fireEvent('activatepane', pane);
28291 pane.el.addClass('active');
28292 pane.fireEvent('activate');
28297 getActivePane : function()
28300 Roo.each(this.panes, function(p) {
28301 if(p.el.hasClass('active')){
28322 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28324 * @class Roo.bootstrap.TabPane
28325 * @extends Roo.bootstrap.Component
28326 * Bootstrap TabPane class
28327 * @cfg {Boolean} active (false | true) Default false
28328 * @cfg {String} title title of panel
28332 * Create a new TabPane
28333 * @param {Object} config The config object
28336 Roo.bootstrap.dash.TabPane = function(config){
28337 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28343 * When a pane is activated
28344 * @param {Roo.bootstrap.dash.TabPane} pane
28351 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28356 // the tabBox that this is attached to.
28359 getAutoCreate : function()
28367 cfg.cls += ' active';
28372 initEvents : function()
28374 //Roo.log('trigger add pane handler');
28375 this.parent().fireEvent('addpane', this)
28379 * Updates the tab title
28380 * @param {String} html to set the title to.
28382 setTitle: function(str)
28388 this.tab.select('a', true).first().dom.innerHTML = str;
28405 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28408 * @class Roo.bootstrap.menu.Menu
28409 * @extends Roo.bootstrap.Component
28410 * Bootstrap Menu class - container for Menu
28411 * @cfg {String} html Text of the menu
28412 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28413 * @cfg {String} icon Font awesome icon
28414 * @cfg {String} pos Menu align to (top | bottom) default bottom
28418 * Create a new Menu
28419 * @param {Object} config The config object
28423 Roo.bootstrap.menu.Menu = function(config){
28424 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28428 * @event beforeshow
28429 * Fires before this menu is displayed
28430 * @param {Roo.bootstrap.menu.Menu} this
28434 * @event beforehide
28435 * Fires before this menu is hidden
28436 * @param {Roo.bootstrap.menu.Menu} this
28441 * Fires after this menu is displayed
28442 * @param {Roo.bootstrap.menu.Menu} this
28447 * Fires after this menu is hidden
28448 * @param {Roo.bootstrap.menu.Menu} this
28453 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28454 * @param {Roo.bootstrap.menu.Menu} this
28455 * @param {Roo.EventObject} e
28462 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28466 weight : 'default',
28471 getChildContainer : function() {
28472 if(this.isSubMenu){
28476 return this.el.select('ul.dropdown-menu', true).first();
28479 getAutoCreate : function()
28484 cls : 'roo-menu-text',
28492 cls : 'fa ' + this.icon
28503 cls : 'dropdown-button btn btn-' + this.weight,
28508 cls : 'dropdown-toggle btn btn-' + this.weight,
28518 cls : 'dropdown-menu'
28524 if(this.pos == 'top'){
28525 cfg.cls += ' dropup';
28528 if(this.isSubMenu){
28531 cls : 'dropdown-menu'
28538 onRender : function(ct, position)
28540 this.isSubMenu = ct.hasClass('dropdown-submenu');
28542 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28545 initEvents : function()
28547 if(this.isSubMenu){
28551 this.hidden = true;
28553 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28554 this.triggerEl.on('click', this.onTriggerPress, this);
28556 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28557 this.buttonEl.on('click', this.onClick, this);
28563 if(this.isSubMenu){
28567 return this.el.select('ul.dropdown-menu', true).first();
28570 onClick : function(e)
28572 this.fireEvent("click", this, e);
28575 onTriggerPress : function(e)
28577 if (this.isVisible()) {
28584 isVisible : function(){
28585 return !this.hidden;
28590 this.fireEvent("beforeshow", this);
28592 this.hidden = false;
28593 this.el.addClass('open');
28595 Roo.get(document).on("mouseup", this.onMouseUp, this);
28597 this.fireEvent("show", this);
28604 this.fireEvent("beforehide", this);
28606 this.hidden = true;
28607 this.el.removeClass('open');
28609 Roo.get(document).un("mouseup", this.onMouseUp);
28611 this.fireEvent("hide", this);
28614 onMouseUp : function()
28628 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28631 * @class Roo.bootstrap.menu.Item
28632 * @extends Roo.bootstrap.Component
28633 * Bootstrap MenuItem class
28634 * @cfg {Boolean} submenu (true | false) default false
28635 * @cfg {String} html text of the item
28636 * @cfg {String} href the link
28637 * @cfg {Boolean} disable (true | false) default false
28638 * @cfg {Boolean} preventDefault (true | false) default true
28639 * @cfg {String} icon Font awesome icon
28640 * @cfg {String} pos Submenu align to (left | right) default right
28644 * Create a new Item
28645 * @param {Object} config The config object
28649 Roo.bootstrap.menu.Item = function(config){
28650 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28654 * Fires when the mouse is hovering over this menu
28655 * @param {Roo.bootstrap.menu.Item} this
28656 * @param {Roo.EventObject} e
28661 * Fires when the mouse exits this menu
28662 * @param {Roo.bootstrap.menu.Item} this
28663 * @param {Roo.EventObject} e
28669 * The raw click event for the entire grid.
28670 * @param {Roo.EventObject} e
28676 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28681 preventDefault: true,
28686 getAutoCreate : function()
28691 cls : 'roo-menu-item-text',
28699 cls : 'fa ' + this.icon
28708 href : this.href || '#',
28715 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28719 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28721 if(this.pos == 'left'){
28722 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28729 initEvents : function()
28731 this.el.on('mouseover', this.onMouseOver, this);
28732 this.el.on('mouseout', this.onMouseOut, this);
28734 this.el.select('a', true).first().on('click', this.onClick, this);
28738 onClick : function(e)
28740 if(this.preventDefault){
28741 e.preventDefault();
28744 this.fireEvent("click", this, e);
28747 onMouseOver : function(e)
28749 if(this.submenu && this.pos == 'left'){
28750 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28753 this.fireEvent("mouseover", this, e);
28756 onMouseOut : function(e)
28758 this.fireEvent("mouseout", this, e);
28770 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28773 * @class Roo.bootstrap.menu.Separator
28774 * @extends Roo.bootstrap.Component
28775 * Bootstrap Separator class
28778 * Create a new Separator
28779 * @param {Object} config The config object
28783 Roo.bootstrap.menu.Separator = function(config){
28784 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28787 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28789 getAutoCreate : function(){
28810 * @class Roo.bootstrap.Tooltip
28811 * Bootstrap Tooltip class
28812 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28813 * to determine which dom element triggers the tooltip.
28815 * It needs to add support for additional attributes like tooltip-position
28818 * Create a new Toolti
28819 * @param {Object} config The config object
28822 Roo.bootstrap.Tooltip = function(config){
28823 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28825 this.alignment = Roo.bootstrap.Tooltip.alignment;
28827 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28828 this.alignment = config.alignment;
28833 Roo.apply(Roo.bootstrap.Tooltip, {
28835 * @function init initialize tooltip monitoring.
28839 currentTip : false,
28840 currentRegion : false,
28846 Roo.get(document).on('mouseover', this.enter ,this);
28847 Roo.get(document).on('mouseout', this.leave, this);
28850 this.currentTip = new Roo.bootstrap.Tooltip();
28853 enter : function(ev)
28855 var dom = ev.getTarget();
28857 //Roo.log(['enter',dom]);
28858 var el = Roo.fly(dom);
28859 if (this.currentEl) {
28861 //Roo.log(this.currentEl);
28862 //Roo.log(this.currentEl.contains(dom));
28863 if (this.currentEl == el) {
28866 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28872 if (this.currentTip.el) {
28873 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28877 if(!el || el.dom == document){
28883 // you can not look for children, as if el is the body.. then everythign is the child..
28884 if (!el.attr('tooltip')) { //
28885 if (!el.select("[tooltip]").elements.length) {
28888 // is the mouse over this child...?
28889 bindEl = el.select("[tooltip]").first();
28890 var xy = ev.getXY();
28891 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28892 //Roo.log("not in region.");
28895 //Roo.log("child element over..");
28898 this.currentEl = bindEl;
28899 this.currentTip.bind(bindEl);
28900 this.currentRegion = Roo.lib.Region.getRegion(dom);
28901 this.currentTip.enter();
28904 leave : function(ev)
28906 var dom = ev.getTarget();
28907 //Roo.log(['leave',dom]);
28908 if (!this.currentEl) {
28913 if (dom != this.currentEl.dom) {
28916 var xy = ev.getXY();
28917 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28920 // only activate leave if mouse cursor is outside... bounding box..
28925 if (this.currentTip) {
28926 this.currentTip.leave();
28928 //Roo.log('clear currentEl');
28929 this.currentEl = false;
28934 'left' : ['r-l', [-2,0], 'right'],
28935 'right' : ['l-r', [2,0], 'left'],
28936 'bottom' : ['t-b', [0,2], 'top'],
28937 'top' : [ 'b-t', [0,-2], 'bottom']
28943 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28948 delay : null, // can be { show : 300 , hide: 500}
28952 hoverState : null, //???
28954 placement : 'bottom',
28958 getAutoCreate : function(){
28965 cls : 'tooltip-arrow arrow'
28968 cls : 'tooltip-inner'
28975 bind : function(el)
28980 initEvents : function()
28982 this.arrowEl = this.el.select('.arrow', true).first();
28983 this.innerEl = this.el.select('.tooltip-inner', true).first();
28986 enter : function () {
28988 if (this.timeout != null) {
28989 clearTimeout(this.timeout);
28992 this.hoverState = 'in';
28993 //Roo.log("enter - show");
28994 if (!this.delay || !this.delay.show) {
28999 this.timeout = setTimeout(function () {
29000 if (_t.hoverState == 'in') {
29003 }, this.delay.show);
29007 clearTimeout(this.timeout);
29009 this.hoverState = 'out';
29010 if (!this.delay || !this.delay.hide) {
29016 this.timeout = setTimeout(function () {
29017 //Roo.log("leave - timeout");
29019 if (_t.hoverState == 'out') {
29021 Roo.bootstrap.Tooltip.currentEl = false;
29026 show : function (msg)
29029 this.render(document.body);
29032 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29034 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29036 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29038 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29039 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29041 var placement = typeof this.placement == 'function' ?
29042 this.placement.call(this, this.el, on_el) :
29045 var autoToken = /\s?auto?\s?/i;
29046 var autoPlace = autoToken.test(placement);
29048 placement = placement.replace(autoToken, '') || 'top';
29052 //this.el.setXY([0,0]);
29054 //this.el.dom.style.display='block';
29056 //this.el.appendTo(on_el);
29058 var p = this.getPosition();
29059 var box = this.el.getBox();
29065 var align = this.alignment[placement];
29067 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29069 if(placement == 'top' || placement == 'bottom'){
29071 placement = 'right';
29074 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29075 placement = 'left';
29078 var scroll = Roo.select('body', true).first().getScroll();
29080 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29084 align = this.alignment[placement];
29086 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29090 this.el.alignTo(this.bindEl, align[0],align[1]);
29091 //var arrow = this.el.select('.arrow',true).first();
29092 //arrow.set(align[2],
29094 this.el.addClass(placement);
29095 this.el.addClass("bs-tooltip-"+ placement);
29097 this.el.addClass('in fade show');
29099 this.hoverState = null;
29101 if (this.el.hasClass('fade')) {
29116 //this.el.setXY([0,0]);
29117 this.el.removeClass(['show', 'in']);
29133 * @class Roo.bootstrap.LocationPicker
29134 * @extends Roo.bootstrap.Component
29135 * Bootstrap LocationPicker class
29136 * @cfg {Number} latitude Position when init default 0
29137 * @cfg {Number} longitude Position when init default 0
29138 * @cfg {Number} zoom default 15
29139 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29140 * @cfg {Boolean} mapTypeControl default false
29141 * @cfg {Boolean} disableDoubleClickZoom default false
29142 * @cfg {Boolean} scrollwheel default true
29143 * @cfg {Boolean} streetViewControl default false
29144 * @cfg {Number} radius default 0
29145 * @cfg {String} locationName
29146 * @cfg {Boolean} draggable default true
29147 * @cfg {Boolean} enableAutocomplete default false
29148 * @cfg {Boolean} enableReverseGeocode default true
29149 * @cfg {String} markerTitle
29152 * Create a new LocationPicker
29153 * @param {Object} config The config object
29157 Roo.bootstrap.LocationPicker = function(config){
29159 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29164 * Fires when the picker initialized.
29165 * @param {Roo.bootstrap.LocationPicker} this
29166 * @param {Google Location} location
29170 * @event positionchanged
29171 * Fires when the picker position changed.
29172 * @param {Roo.bootstrap.LocationPicker} this
29173 * @param {Google Location} location
29175 positionchanged : true,
29178 * Fires when the map resize.
29179 * @param {Roo.bootstrap.LocationPicker} this
29184 * Fires when the map show.
29185 * @param {Roo.bootstrap.LocationPicker} this
29190 * Fires when the map hide.
29191 * @param {Roo.bootstrap.LocationPicker} this
29196 * Fires when click the map.
29197 * @param {Roo.bootstrap.LocationPicker} this
29198 * @param {Map event} e
29202 * @event mapRightClick
29203 * Fires when right click the map.
29204 * @param {Roo.bootstrap.LocationPicker} this
29205 * @param {Map event} e
29207 mapRightClick : true,
29209 * @event markerClick
29210 * Fires when click the marker.
29211 * @param {Roo.bootstrap.LocationPicker} this
29212 * @param {Map event} e
29214 markerClick : true,
29216 * @event markerRightClick
29217 * Fires when right click the marker.
29218 * @param {Roo.bootstrap.LocationPicker} this
29219 * @param {Map event} e
29221 markerRightClick : true,
29223 * @event OverlayViewDraw
29224 * Fires when OverlayView Draw
29225 * @param {Roo.bootstrap.LocationPicker} this
29227 OverlayViewDraw : true,
29229 * @event OverlayViewOnAdd
29230 * Fires when OverlayView Draw
29231 * @param {Roo.bootstrap.LocationPicker} this
29233 OverlayViewOnAdd : true,
29235 * @event OverlayViewOnRemove
29236 * Fires when OverlayView Draw
29237 * @param {Roo.bootstrap.LocationPicker} this
29239 OverlayViewOnRemove : true,
29241 * @event OverlayViewShow
29242 * Fires when OverlayView Draw
29243 * @param {Roo.bootstrap.LocationPicker} this
29244 * @param {Pixel} cpx
29246 OverlayViewShow : true,
29248 * @event OverlayViewHide
29249 * Fires when OverlayView Draw
29250 * @param {Roo.bootstrap.LocationPicker} this
29252 OverlayViewHide : true,
29254 * @event loadexception
29255 * Fires when load google lib failed.
29256 * @param {Roo.bootstrap.LocationPicker} this
29258 loadexception : true
29263 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29265 gMapContext: false,
29271 mapTypeControl: false,
29272 disableDoubleClickZoom: false,
29274 streetViewControl: false,
29278 enableAutocomplete: false,
29279 enableReverseGeocode: true,
29282 getAutoCreate: function()
29287 cls: 'roo-location-picker'
29293 initEvents: function(ct, position)
29295 if(!this.el.getWidth() || this.isApplied()){
29299 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29304 initial: function()
29306 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29307 this.fireEvent('loadexception', this);
29311 if(!this.mapTypeId){
29312 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29315 this.gMapContext = this.GMapContext();
29317 this.initOverlayView();
29319 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29323 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29324 _this.setPosition(_this.gMapContext.marker.position);
29327 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29328 _this.fireEvent('mapClick', this, event);
29332 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29333 _this.fireEvent('mapRightClick', this, event);
29337 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29338 _this.fireEvent('markerClick', this, event);
29342 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29343 _this.fireEvent('markerRightClick', this, event);
29347 this.setPosition(this.gMapContext.location);
29349 this.fireEvent('initial', this, this.gMapContext.location);
29352 initOverlayView: function()
29356 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29360 _this.fireEvent('OverlayViewDraw', _this);
29365 _this.fireEvent('OverlayViewOnAdd', _this);
29368 onRemove: function()
29370 _this.fireEvent('OverlayViewOnRemove', _this);
29373 show: function(cpx)
29375 _this.fireEvent('OverlayViewShow', _this, cpx);
29380 _this.fireEvent('OverlayViewHide', _this);
29386 fromLatLngToContainerPixel: function(event)
29388 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29391 isApplied: function()
29393 return this.getGmapContext() == false ? false : true;
29396 getGmapContext: function()
29398 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29401 GMapContext: function()
29403 var position = new google.maps.LatLng(this.latitude, this.longitude);
29405 var _map = new google.maps.Map(this.el.dom, {
29408 mapTypeId: this.mapTypeId,
29409 mapTypeControl: this.mapTypeControl,
29410 disableDoubleClickZoom: this.disableDoubleClickZoom,
29411 scrollwheel: this.scrollwheel,
29412 streetViewControl: this.streetViewControl,
29413 locationName: this.locationName,
29414 draggable: this.draggable,
29415 enableAutocomplete: this.enableAutocomplete,
29416 enableReverseGeocode: this.enableReverseGeocode
29419 var _marker = new google.maps.Marker({
29420 position: position,
29422 title: this.markerTitle,
29423 draggable: this.draggable
29430 location: position,
29431 radius: this.radius,
29432 locationName: this.locationName,
29433 addressComponents: {
29434 formatted_address: null,
29435 addressLine1: null,
29436 addressLine2: null,
29438 streetNumber: null,
29442 stateOrProvince: null
29445 domContainer: this.el.dom,
29446 geodecoder: new google.maps.Geocoder()
29450 drawCircle: function(center, radius, options)
29452 if (this.gMapContext.circle != null) {
29453 this.gMapContext.circle.setMap(null);
29457 options = Roo.apply({}, options, {
29458 strokeColor: "#0000FF",
29459 strokeOpacity: .35,
29461 fillColor: "#0000FF",
29465 options.map = this.gMapContext.map;
29466 options.radius = radius;
29467 options.center = center;
29468 this.gMapContext.circle = new google.maps.Circle(options);
29469 return this.gMapContext.circle;
29475 setPosition: function(location)
29477 this.gMapContext.location = location;
29478 this.gMapContext.marker.setPosition(location);
29479 this.gMapContext.map.panTo(location);
29480 this.drawCircle(location, this.gMapContext.radius, {});
29484 if (this.gMapContext.settings.enableReverseGeocode) {
29485 this.gMapContext.geodecoder.geocode({
29486 latLng: this.gMapContext.location
29487 }, function(results, status) {
29489 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29490 _this.gMapContext.locationName = results[0].formatted_address;
29491 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29493 _this.fireEvent('positionchanged', this, location);
29500 this.fireEvent('positionchanged', this, location);
29505 google.maps.event.trigger(this.gMapContext.map, "resize");
29507 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29509 this.fireEvent('resize', this);
29512 setPositionByLatLng: function(latitude, longitude)
29514 this.setPosition(new google.maps.LatLng(latitude, longitude));
29517 getCurrentPosition: function()
29520 latitude: this.gMapContext.location.lat(),
29521 longitude: this.gMapContext.location.lng()
29525 getAddressName: function()
29527 return this.gMapContext.locationName;
29530 getAddressComponents: function()
29532 return this.gMapContext.addressComponents;
29535 address_component_from_google_geocode: function(address_components)
29539 for (var i = 0; i < address_components.length; i++) {
29540 var component = address_components[i];
29541 if (component.types.indexOf("postal_code") >= 0) {
29542 result.postalCode = component.short_name;
29543 } else if (component.types.indexOf("street_number") >= 0) {
29544 result.streetNumber = component.short_name;
29545 } else if (component.types.indexOf("route") >= 0) {
29546 result.streetName = component.short_name;
29547 } else if (component.types.indexOf("neighborhood") >= 0) {
29548 result.city = component.short_name;
29549 } else if (component.types.indexOf("locality") >= 0) {
29550 result.city = component.short_name;
29551 } else if (component.types.indexOf("sublocality") >= 0) {
29552 result.district = component.short_name;
29553 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29554 result.stateOrProvince = component.short_name;
29555 } else if (component.types.indexOf("country") >= 0) {
29556 result.country = component.short_name;
29560 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29561 result.addressLine2 = "";
29565 setZoomLevel: function(zoom)
29567 this.gMapContext.map.setZoom(zoom);
29580 this.fireEvent('show', this);
29591 this.fireEvent('hide', this);
29596 Roo.apply(Roo.bootstrap.LocationPicker, {
29598 OverlayView : function(map, options)
29600 options = options || {};
29607 * @class Roo.bootstrap.Alert
29608 * @extends Roo.bootstrap.Component
29609 * Bootstrap Alert class - shows an alert area box
29611 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29612 Enter a valid email address
29615 * @cfg {String} title The title of alert
29616 * @cfg {String} html The content of alert
29617 * @cfg {String} weight ( success | info | warning | danger )
29618 * @cfg {String} faicon font-awesomeicon
29621 * Create a new alert
29622 * @param {Object} config The config object
29626 Roo.bootstrap.Alert = function(config){
29627 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29631 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29638 getAutoCreate : function()
29647 cls : 'roo-alert-icon'
29652 cls : 'roo-alert-title',
29657 cls : 'roo-alert-text',
29664 cfg.cn[0].cls += ' fa ' + this.faicon;
29668 cfg.cls += ' alert-' + this.weight;
29674 initEvents: function()
29676 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29679 setTitle : function(str)
29681 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29684 setText : function(str)
29686 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29689 setWeight : function(weight)
29692 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29695 this.weight = weight;
29697 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29700 setIcon : function(icon)
29703 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29706 this.faicon = icon;
29708 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29729 * @class Roo.bootstrap.UploadCropbox
29730 * @extends Roo.bootstrap.Component
29731 * Bootstrap UploadCropbox class
29732 * @cfg {String} emptyText show when image has been loaded
29733 * @cfg {String} rotateNotify show when image too small to rotate
29734 * @cfg {Number} errorTimeout default 3000
29735 * @cfg {Number} minWidth default 300
29736 * @cfg {Number} minHeight default 300
29737 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29738 * @cfg {Boolean} isDocument (true|false) default false
29739 * @cfg {String} url action url
29740 * @cfg {String} paramName default 'imageUpload'
29741 * @cfg {String} method default POST
29742 * @cfg {Boolean} loadMask (true|false) default true
29743 * @cfg {Boolean} loadingText default 'Loading...'
29746 * Create a new UploadCropbox
29747 * @param {Object} config The config object
29750 Roo.bootstrap.UploadCropbox = function(config){
29751 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29755 * @event beforeselectfile
29756 * Fire before select file
29757 * @param {Roo.bootstrap.UploadCropbox} this
29759 "beforeselectfile" : true,
29762 * Fire after initEvent
29763 * @param {Roo.bootstrap.UploadCropbox} this
29768 * Fire after initEvent
29769 * @param {Roo.bootstrap.UploadCropbox} this
29770 * @param {String} data
29775 * Fire when preparing the file data
29776 * @param {Roo.bootstrap.UploadCropbox} this
29777 * @param {Object} file
29782 * Fire when get exception
29783 * @param {Roo.bootstrap.UploadCropbox} this
29784 * @param {XMLHttpRequest} xhr
29786 "exception" : true,
29788 * @event beforeloadcanvas
29789 * Fire before load the canvas
29790 * @param {Roo.bootstrap.UploadCropbox} this
29791 * @param {String} src
29793 "beforeloadcanvas" : true,
29796 * Fire when trash image
29797 * @param {Roo.bootstrap.UploadCropbox} this
29802 * Fire when download the image
29803 * @param {Roo.bootstrap.UploadCropbox} this
29807 * @event footerbuttonclick
29808 * Fire when footerbuttonclick
29809 * @param {Roo.bootstrap.UploadCropbox} this
29810 * @param {String} type
29812 "footerbuttonclick" : true,
29816 * @param {Roo.bootstrap.UploadCropbox} this
29821 * Fire when rotate the image
29822 * @param {Roo.bootstrap.UploadCropbox} this
29823 * @param {String} pos
29828 * Fire when inspect the file
29829 * @param {Roo.bootstrap.UploadCropbox} this
29830 * @param {Object} file
29835 * Fire when xhr upload the file
29836 * @param {Roo.bootstrap.UploadCropbox} this
29837 * @param {Object} data
29842 * Fire when arrange the file data
29843 * @param {Roo.bootstrap.UploadCropbox} this
29844 * @param {Object} formData
29849 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29852 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29854 emptyText : 'Click to upload image',
29855 rotateNotify : 'Image is too small to rotate',
29856 errorTimeout : 3000,
29870 cropType : 'image/jpeg',
29872 canvasLoaded : false,
29873 isDocument : false,
29875 paramName : 'imageUpload',
29877 loadingText : 'Loading...',
29880 getAutoCreate : function()
29884 cls : 'roo-upload-cropbox',
29888 cls : 'roo-upload-cropbox-selector',
29893 cls : 'roo-upload-cropbox-body',
29894 style : 'cursor:pointer',
29898 cls : 'roo-upload-cropbox-preview'
29902 cls : 'roo-upload-cropbox-thumb'
29906 cls : 'roo-upload-cropbox-empty-notify',
29907 html : this.emptyText
29911 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29912 html : this.rotateNotify
29918 cls : 'roo-upload-cropbox-footer',
29921 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29931 onRender : function(ct, position)
29933 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29935 if (this.buttons.length) {
29937 Roo.each(this.buttons, function(bb) {
29939 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29941 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29947 this.maskEl = this.el;
29951 initEvents : function()
29953 this.urlAPI = (window.createObjectURL && window) ||
29954 (window.URL && URL.revokeObjectURL && URL) ||
29955 (window.webkitURL && webkitURL);
29957 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29958 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29960 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29961 this.selectorEl.hide();
29963 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29964 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29966 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29967 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29968 this.thumbEl.hide();
29970 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29971 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29973 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29974 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29975 this.errorEl.hide();
29977 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29978 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29979 this.footerEl.hide();
29981 this.setThumbBoxSize();
29987 this.fireEvent('initial', this);
29994 window.addEventListener("resize", function() { _this.resize(); } );
29996 this.bodyEl.on('click', this.beforeSelectFile, this);
29999 this.bodyEl.on('touchstart', this.onTouchStart, this);
30000 this.bodyEl.on('touchmove', this.onTouchMove, this);
30001 this.bodyEl.on('touchend', this.onTouchEnd, this);
30005 this.bodyEl.on('mousedown', this.onMouseDown, this);
30006 this.bodyEl.on('mousemove', this.onMouseMove, this);
30007 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30008 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30009 Roo.get(document).on('mouseup', this.onMouseUp, this);
30012 this.selectorEl.on('change', this.onFileSelected, this);
30018 this.baseScale = 1;
30020 this.baseRotate = 1;
30021 this.dragable = false;
30022 this.pinching = false;
30025 this.cropData = false;
30026 this.notifyEl.dom.innerHTML = this.emptyText;
30028 this.selectorEl.dom.value = '';
30032 resize : function()
30034 if(this.fireEvent('resize', this) != false){
30035 this.setThumbBoxPosition();
30036 this.setCanvasPosition();
30040 onFooterButtonClick : function(e, el, o, type)
30043 case 'rotate-left' :
30044 this.onRotateLeft(e);
30046 case 'rotate-right' :
30047 this.onRotateRight(e);
30050 this.beforeSelectFile(e);
30065 this.fireEvent('footerbuttonclick', this, type);
30068 beforeSelectFile : function(e)
30070 e.preventDefault();
30072 if(this.fireEvent('beforeselectfile', this) != false){
30073 this.selectorEl.dom.click();
30077 onFileSelected : function(e)
30079 e.preventDefault();
30081 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30085 var file = this.selectorEl.dom.files[0];
30087 if(this.fireEvent('inspect', this, file) != false){
30088 this.prepare(file);
30093 trash : function(e)
30095 this.fireEvent('trash', this);
30098 download : function(e)
30100 this.fireEvent('download', this);
30103 loadCanvas : function(src)
30105 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30109 this.imageEl = document.createElement('img');
30113 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30115 this.imageEl.src = src;
30119 onLoadCanvas : function()
30121 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30122 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30124 this.bodyEl.un('click', this.beforeSelectFile, this);
30126 this.notifyEl.hide();
30127 this.thumbEl.show();
30128 this.footerEl.show();
30130 this.baseRotateLevel();
30132 if(this.isDocument){
30133 this.setThumbBoxSize();
30136 this.setThumbBoxPosition();
30138 this.baseScaleLevel();
30144 this.canvasLoaded = true;
30147 this.maskEl.unmask();
30152 setCanvasPosition : function()
30154 if(!this.canvasEl){
30158 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30159 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30161 this.previewEl.setLeft(pw);
30162 this.previewEl.setTop(ph);
30166 onMouseDown : function(e)
30170 this.dragable = true;
30171 this.pinching = false;
30173 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30174 this.dragable = false;
30178 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30179 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30183 onMouseMove : function(e)
30187 if(!this.canvasLoaded){
30191 if (!this.dragable){
30195 var minX = Math.ceil(this.thumbEl.getLeft(true));
30196 var minY = Math.ceil(this.thumbEl.getTop(true));
30198 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30199 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30201 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30202 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30204 x = x - this.mouseX;
30205 y = y - this.mouseY;
30207 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30208 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30210 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30211 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30213 this.previewEl.setLeft(bgX);
30214 this.previewEl.setTop(bgY);
30216 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30217 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30220 onMouseUp : function(e)
30224 this.dragable = false;
30227 onMouseWheel : function(e)
30231 this.startScale = this.scale;
30233 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30235 if(!this.zoomable()){
30236 this.scale = this.startScale;
30245 zoomable : function()
30247 var minScale = this.thumbEl.getWidth() / this.minWidth;
30249 if(this.minWidth < this.minHeight){
30250 minScale = this.thumbEl.getHeight() / this.minHeight;
30253 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30254 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30258 (this.rotate == 0 || this.rotate == 180) &&
30260 width > this.imageEl.OriginWidth ||
30261 height > this.imageEl.OriginHeight ||
30262 (width < this.minWidth && height < this.minHeight)
30270 (this.rotate == 90 || this.rotate == 270) &&
30272 width > this.imageEl.OriginWidth ||
30273 height > this.imageEl.OriginHeight ||
30274 (width < this.minHeight && height < this.minWidth)
30281 !this.isDocument &&
30282 (this.rotate == 0 || this.rotate == 180) &&
30284 width < this.minWidth ||
30285 width > this.imageEl.OriginWidth ||
30286 height < this.minHeight ||
30287 height > this.imageEl.OriginHeight
30294 !this.isDocument &&
30295 (this.rotate == 90 || this.rotate == 270) &&
30297 width < this.minHeight ||
30298 width > this.imageEl.OriginWidth ||
30299 height < this.minWidth ||
30300 height > this.imageEl.OriginHeight
30310 onRotateLeft : function(e)
30312 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30314 var minScale = this.thumbEl.getWidth() / this.minWidth;
30316 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30317 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30319 this.startScale = this.scale;
30321 while (this.getScaleLevel() < minScale){
30323 this.scale = this.scale + 1;
30325 if(!this.zoomable()){
30330 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30331 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30336 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30343 this.scale = this.startScale;
30345 this.onRotateFail();
30350 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30352 if(this.isDocument){
30353 this.setThumbBoxSize();
30354 this.setThumbBoxPosition();
30355 this.setCanvasPosition();
30360 this.fireEvent('rotate', this, 'left');
30364 onRotateRight : function(e)
30366 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30368 var minScale = this.thumbEl.getWidth() / this.minWidth;
30370 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30371 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30373 this.startScale = this.scale;
30375 while (this.getScaleLevel() < minScale){
30377 this.scale = this.scale + 1;
30379 if(!this.zoomable()){
30384 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30385 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30390 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30397 this.scale = this.startScale;
30399 this.onRotateFail();
30404 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30406 if(this.isDocument){
30407 this.setThumbBoxSize();
30408 this.setThumbBoxPosition();
30409 this.setCanvasPosition();
30414 this.fireEvent('rotate', this, 'right');
30417 onRotateFail : function()
30419 this.errorEl.show(true);
30423 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30428 this.previewEl.dom.innerHTML = '';
30430 var canvasEl = document.createElement("canvas");
30432 var contextEl = canvasEl.getContext("2d");
30434 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30435 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30436 var center = this.imageEl.OriginWidth / 2;
30438 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30439 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30440 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30441 center = this.imageEl.OriginHeight / 2;
30444 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30446 contextEl.translate(center, center);
30447 contextEl.rotate(this.rotate * Math.PI / 180);
30449 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30451 this.canvasEl = document.createElement("canvas");
30453 this.contextEl = this.canvasEl.getContext("2d");
30455 switch (this.rotate) {
30458 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30459 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30461 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30466 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30467 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30469 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30470 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);
30474 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30479 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30480 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30482 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30483 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);
30487 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);
30492 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30493 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30495 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30496 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30500 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);
30507 this.previewEl.appendChild(this.canvasEl);
30509 this.setCanvasPosition();
30514 if(!this.canvasLoaded){
30518 var imageCanvas = document.createElement("canvas");
30520 var imageContext = imageCanvas.getContext("2d");
30522 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30523 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30525 var center = imageCanvas.width / 2;
30527 imageContext.translate(center, center);
30529 imageContext.rotate(this.rotate * Math.PI / 180);
30531 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30533 var canvas = document.createElement("canvas");
30535 var context = canvas.getContext("2d");
30537 canvas.width = this.minWidth;
30538 canvas.height = this.minHeight;
30540 switch (this.rotate) {
30543 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30544 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30546 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30547 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30549 var targetWidth = this.minWidth - 2 * x;
30550 var targetHeight = this.minHeight - 2 * y;
30554 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30555 scale = targetWidth / width;
30558 if(x > 0 && y == 0){
30559 scale = targetHeight / height;
30562 if(x > 0 && y > 0){
30563 scale = targetWidth / width;
30565 if(width < height){
30566 scale = targetHeight / height;
30570 context.scale(scale, scale);
30572 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30573 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30575 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30576 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30578 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30583 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30584 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30586 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30587 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30589 var targetWidth = this.minWidth - 2 * x;
30590 var targetHeight = this.minHeight - 2 * y;
30594 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30595 scale = targetWidth / width;
30598 if(x > 0 && y == 0){
30599 scale = targetHeight / height;
30602 if(x > 0 && y > 0){
30603 scale = targetWidth / width;
30605 if(width < height){
30606 scale = targetHeight / height;
30610 context.scale(scale, scale);
30612 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30613 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30615 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30616 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30618 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30620 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30625 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30626 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30628 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30629 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30631 var targetWidth = this.minWidth - 2 * x;
30632 var targetHeight = this.minHeight - 2 * y;
30636 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30637 scale = targetWidth / width;
30640 if(x > 0 && y == 0){
30641 scale = targetHeight / height;
30644 if(x > 0 && y > 0){
30645 scale = targetWidth / width;
30647 if(width < height){
30648 scale = targetHeight / height;
30652 context.scale(scale, scale);
30654 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30655 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30657 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30658 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30660 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30661 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30663 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30668 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30669 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30671 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30672 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30674 var targetWidth = this.minWidth - 2 * x;
30675 var targetHeight = this.minHeight - 2 * y;
30679 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30680 scale = targetWidth / width;
30683 if(x > 0 && y == 0){
30684 scale = targetHeight / height;
30687 if(x > 0 && y > 0){
30688 scale = targetWidth / width;
30690 if(width < height){
30691 scale = targetHeight / height;
30695 context.scale(scale, scale);
30697 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30698 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30700 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30701 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30703 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30705 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30712 this.cropData = canvas.toDataURL(this.cropType);
30714 if(this.fireEvent('crop', this, this.cropData) !== false){
30715 this.process(this.file, this.cropData);
30722 setThumbBoxSize : function()
30726 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30727 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30728 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30730 this.minWidth = width;
30731 this.minHeight = height;
30733 if(this.rotate == 90 || this.rotate == 270){
30734 this.minWidth = height;
30735 this.minHeight = width;
30740 width = Math.ceil(this.minWidth * height / this.minHeight);
30742 if(this.minWidth > this.minHeight){
30744 height = Math.ceil(this.minHeight * width / this.minWidth);
30747 this.thumbEl.setStyle({
30748 width : width + 'px',
30749 height : height + 'px'
30756 setThumbBoxPosition : function()
30758 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30759 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30761 this.thumbEl.setLeft(x);
30762 this.thumbEl.setTop(y);
30766 baseRotateLevel : function()
30768 this.baseRotate = 1;
30771 typeof(this.exif) != 'undefined' &&
30772 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30773 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30775 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30778 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30782 baseScaleLevel : function()
30786 if(this.isDocument){
30788 if(this.baseRotate == 6 || this.baseRotate == 8){
30790 height = this.thumbEl.getHeight();
30791 this.baseScale = height / this.imageEl.OriginWidth;
30793 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30794 width = this.thumbEl.getWidth();
30795 this.baseScale = width / this.imageEl.OriginHeight;
30801 height = this.thumbEl.getHeight();
30802 this.baseScale = height / this.imageEl.OriginHeight;
30804 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30805 width = this.thumbEl.getWidth();
30806 this.baseScale = width / this.imageEl.OriginWidth;
30812 if(this.baseRotate == 6 || this.baseRotate == 8){
30814 width = this.thumbEl.getHeight();
30815 this.baseScale = width / this.imageEl.OriginHeight;
30817 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30818 height = this.thumbEl.getWidth();
30819 this.baseScale = height / this.imageEl.OriginHeight;
30822 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30823 height = this.thumbEl.getWidth();
30824 this.baseScale = height / this.imageEl.OriginHeight;
30826 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30827 width = this.thumbEl.getHeight();
30828 this.baseScale = width / this.imageEl.OriginWidth;
30835 width = this.thumbEl.getWidth();
30836 this.baseScale = width / this.imageEl.OriginWidth;
30838 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30839 height = this.thumbEl.getHeight();
30840 this.baseScale = height / this.imageEl.OriginHeight;
30843 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30845 height = this.thumbEl.getHeight();
30846 this.baseScale = height / this.imageEl.OriginHeight;
30848 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30849 width = this.thumbEl.getWidth();
30850 this.baseScale = width / this.imageEl.OriginWidth;
30858 getScaleLevel : function()
30860 return this.baseScale * Math.pow(1.1, this.scale);
30863 onTouchStart : function(e)
30865 if(!this.canvasLoaded){
30866 this.beforeSelectFile(e);
30870 var touches = e.browserEvent.touches;
30876 if(touches.length == 1){
30877 this.onMouseDown(e);
30881 if(touches.length != 2){
30887 for(var i = 0, finger; finger = touches[i]; i++){
30888 coords.push(finger.pageX, finger.pageY);
30891 var x = Math.pow(coords[0] - coords[2], 2);
30892 var y = Math.pow(coords[1] - coords[3], 2);
30894 this.startDistance = Math.sqrt(x + y);
30896 this.startScale = this.scale;
30898 this.pinching = true;
30899 this.dragable = false;
30903 onTouchMove : function(e)
30905 if(!this.pinching && !this.dragable){
30909 var touches = e.browserEvent.touches;
30916 this.onMouseMove(e);
30922 for(var i = 0, finger; finger = touches[i]; i++){
30923 coords.push(finger.pageX, finger.pageY);
30926 var x = Math.pow(coords[0] - coords[2], 2);
30927 var y = Math.pow(coords[1] - coords[3], 2);
30929 this.endDistance = Math.sqrt(x + y);
30931 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30933 if(!this.zoomable()){
30934 this.scale = this.startScale;
30942 onTouchEnd : function(e)
30944 this.pinching = false;
30945 this.dragable = false;
30949 process : function(file, crop)
30952 this.maskEl.mask(this.loadingText);
30955 this.xhr = new XMLHttpRequest();
30957 file.xhr = this.xhr;
30959 this.xhr.open(this.method, this.url, true);
30962 "Accept": "application/json",
30963 "Cache-Control": "no-cache",
30964 "X-Requested-With": "XMLHttpRequest"
30967 for (var headerName in headers) {
30968 var headerValue = headers[headerName];
30970 this.xhr.setRequestHeader(headerName, headerValue);
30976 this.xhr.onload = function()
30978 _this.xhrOnLoad(_this.xhr);
30981 this.xhr.onerror = function()
30983 _this.xhrOnError(_this.xhr);
30986 var formData = new FormData();
30988 formData.append('returnHTML', 'NO');
30991 formData.append('crop', crop);
30994 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30995 formData.append(this.paramName, file, file.name);
30998 if(typeof(file.filename) != 'undefined'){
30999 formData.append('filename', file.filename);
31002 if(typeof(file.mimetype) != 'undefined'){
31003 formData.append('mimetype', file.mimetype);
31006 if(this.fireEvent('arrange', this, formData) != false){
31007 this.xhr.send(formData);
31011 xhrOnLoad : function(xhr)
31014 this.maskEl.unmask();
31017 if (xhr.readyState !== 4) {
31018 this.fireEvent('exception', this, xhr);
31022 var response = Roo.decode(xhr.responseText);
31024 if(!response.success){
31025 this.fireEvent('exception', this, xhr);
31029 var response = Roo.decode(xhr.responseText);
31031 this.fireEvent('upload', this, response);
31035 xhrOnError : function()
31038 this.maskEl.unmask();
31041 Roo.log('xhr on error');
31043 var response = Roo.decode(xhr.responseText);
31049 prepare : function(file)
31052 this.maskEl.mask(this.loadingText);
31058 if(typeof(file) === 'string'){
31059 this.loadCanvas(file);
31063 if(!file || !this.urlAPI){
31068 this.cropType = file.type;
31072 if(this.fireEvent('prepare', this, this.file) != false){
31074 var reader = new FileReader();
31076 reader.onload = function (e) {
31077 if (e.target.error) {
31078 Roo.log(e.target.error);
31082 var buffer = e.target.result,
31083 dataView = new DataView(buffer),
31085 maxOffset = dataView.byteLength - 4,
31089 if (dataView.getUint16(0) === 0xffd8) {
31090 while (offset < maxOffset) {
31091 markerBytes = dataView.getUint16(offset);
31093 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31094 markerLength = dataView.getUint16(offset + 2) + 2;
31095 if (offset + markerLength > dataView.byteLength) {
31096 Roo.log('Invalid meta data: Invalid segment size.');
31100 if(markerBytes == 0xffe1){
31101 _this.parseExifData(
31108 offset += markerLength;
31118 var url = _this.urlAPI.createObjectURL(_this.file);
31120 _this.loadCanvas(url);
31125 reader.readAsArrayBuffer(this.file);
31131 parseExifData : function(dataView, offset, length)
31133 var tiffOffset = offset + 10,
31137 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31138 // No Exif data, might be XMP data instead
31142 // Check for the ASCII code for "Exif" (0x45786966):
31143 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31144 // No Exif data, might be XMP data instead
31147 if (tiffOffset + 8 > dataView.byteLength) {
31148 Roo.log('Invalid Exif data: Invalid segment size.');
31151 // Check for the two null bytes:
31152 if (dataView.getUint16(offset + 8) !== 0x0000) {
31153 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31156 // Check the byte alignment:
31157 switch (dataView.getUint16(tiffOffset)) {
31159 littleEndian = true;
31162 littleEndian = false;
31165 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31168 // Check for the TIFF tag marker (0x002A):
31169 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31170 Roo.log('Invalid Exif data: Missing TIFF marker.');
31173 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31174 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31176 this.parseExifTags(
31179 tiffOffset + dirOffset,
31184 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31189 if (dirOffset + 6 > dataView.byteLength) {
31190 Roo.log('Invalid Exif data: Invalid directory offset.');
31193 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31194 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31195 if (dirEndOffset + 4 > dataView.byteLength) {
31196 Roo.log('Invalid Exif data: Invalid directory size.');
31199 for (i = 0; i < tagsNumber; i += 1) {
31203 dirOffset + 2 + 12 * i, // tag offset
31207 // Return the offset to the next directory:
31208 return dataView.getUint32(dirEndOffset, littleEndian);
31211 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31213 var tag = dataView.getUint16(offset, littleEndian);
31215 this.exif[tag] = this.getExifValue(
31219 dataView.getUint16(offset + 2, littleEndian), // tag type
31220 dataView.getUint32(offset + 4, littleEndian), // tag length
31225 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31227 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31236 Roo.log('Invalid Exif data: Invalid tag type.');
31240 tagSize = tagType.size * length;
31241 // Determine if the value is contained in the dataOffset bytes,
31242 // or if the value at the dataOffset is a pointer to the actual data:
31243 dataOffset = tagSize > 4 ?
31244 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31245 if (dataOffset + tagSize > dataView.byteLength) {
31246 Roo.log('Invalid Exif data: Invalid data offset.');
31249 if (length === 1) {
31250 return tagType.getValue(dataView, dataOffset, littleEndian);
31253 for (i = 0; i < length; i += 1) {
31254 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31257 if (tagType.ascii) {
31259 // Concatenate the chars:
31260 for (i = 0; i < values.length; i += 1) {
31262 // Ignore the terminating NULL byte(s):
31263 if (c === '\u0000') {
31275 Roo.apply(Roo.bootstrap.UploadCropbox, {
31277 'Orientation': 0x0112
31281 1: 0, //'top-left',
31283 3: 180, //'bottom-right',
31284 // 4: 'bottom-left',
31286 6: 90, //'right-top',
31287 // 7: 'right-bottom',
31288 8: 270 //'left-bottom'
31292 // byte, 8-bit unsigned int:
31294 getValue: function (dataView, dataOffset) {
31295 return dataView.getUint8(dataOffset);
31299 // ascii, 8-bit byte:
31301 getValue: function (dataView, dataOffset) {
31302 return String.fromCharCode(dataView.getUint8(dataOffset));
31307 // short, 16 bit int:
31309 getValue: function (dataView, dataOffset, littleEndian) {
31310 return dataView.getUint16(dataOffset, littleEndian);
31314 // long, 32 bit int:
31316 getValue: function (dataView, dataOffset, littleEndian) {
31317 return dataView.getUint32(dataOffset, littleEndian);
31321 // rational = two long values, first is numerator, second is denominator:
31323 getValue: function (dataView, dataOffset, littleEndian) {
31324 return dataView.getUint32(dataOffset, littleEndian) /
31325 dataView.getUint32(dataOffset + 4, littleEndian);
31329 // slong, 32 bit signed int:
31331 getValue: function (dataView, dataOffset, littleEndian) {
31332 return dataView.getInt32(dataOffset, littleEndian);
31336 // srational, two slongs, first is numerator, second is denominator:
31338 getValue: function (dataView, dataOffset, littleEndian) {
31339 return dataView.getInt32(dataOffset, littleEndian) /
31340 dataView.getInt32(dataOffset + 4, littleEndian);
31350 cls : 'btn-group roo-upload-cropbox-rotate-left',
31351 action : 'rotate-left',
31355 cls : 'btn btn-default',
31356 html : '<i class="fa fa-undo"></i>'
31362 cls : 'btn-group roo-upload-cropbox-picture',
31363 action : 'picture',
31367 cls : 'btn btn-default',
31368 html : '<i class="fa fa-picture-o"></i>'
31374 cls : 'btn-group roo-upload-cropbox-rotate-right',
31375 action : 'rotate-right',
31379 cls : 'btn btn-default',
31380 html : '<i class="fa fa-repeat"></i>'
31388 cls : 'btn-group roo-upload-cropbox-rotate-left',
31389 action : 'rotate-left',
31393 cls : 'btn btn-default',
31394 html : '<i class="fa fa-undo"></i>'
31400 cls : 'btn-group roo-upload-cropbox-download',
31401 action : 'download',
31405 cls : 'btn btn-default',
31406 html : '<i class="fa fa-download"></i>'
31412 cls : 'btn-group roo-upload-cropbox-crop',
31417 cls : 'btn btn-default',
31418 html : '<i class="fa fa-crop"></i>'
31424 cls : 'btn-group roo-upload-cropbox-trash',
31429 cls : 'btn btn-default',
31430 html : '<i class="fa fa-trash"></i>'
31436 cls : 'btn-group roo-upload-cropbox-rotate-right',
31437 action : 'rotate-right',
31441 cls : 'btn btn-default',
31442 html : '<i class="fa fa-repeat"></i>'
31450 cls : 'btn-group roo-upload-cropbox-rotate-left',
31451 action : 'rotate-left',
31455 cls : 'btn btn-default',
31456 html : '<i class="fa fa-undo"></i>'
31462 cls : 'btn-group roo-upload-cropbox-rotate-right',
31463 action : 'rotate-right',
31467 cls : 'btn btn-default',
31468 html : '<i class="fa fa-repeat"></i>'
31481 * @class Roo.bootstrap.DocumentManager
31482 * @extends Roo.bootstrap.Component
31483 * Bootstrap DocumentManager class
31484 * @cfg {String} paramName default 'imageUpload'
31485 * @cfg {String} toolTipName default 'filename'
31486 * @cfg {String} method default POST
31487 * @cfg {String} url action url
31488 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31489 * @cfg {Boolean} multiple multiple upload default true
31490 * @cfg {Number} thumbSize default 300
31491 * @cfg {String} fieldLabel
31492 * @cfg {Number} labelWidth default 4
31493 * @cfg {String} labelAlign (left|top) default left
31494 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31495 * @cfg {Number} labellg set the width of label (1-12)
31496 * @cfg {Number} labelmd set the width of label (1-12)
31497 * @cfg {Number} labelsm set the width of label (1-12)
31498 * @cfg {Number} labelxs set the width of label (1-12)
31501 * Create a new DocumentManager
31502 * @param {Object} config The config object
31505 Roo.bootstrap.DocumentManager = function(config){
31506 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31509 this.delegates = [];
31514 * Fire when initial the DocumentManager
31515 * @param {Roo.bootstrap.DocumentManager} this
31520 * inspect selected file
31521 * @param {Roo.bootstrap.DocumentManager} this
31522 * @param {File} file
31527 * Fire when xhr load exception
31528 * @param {Roo.bootstrap.DocumentManager} this
31529 * @param {XMLHttpRequest} xhr
31531 "exception" : true,
31533 * @event afterupload
31534 * Fire when xhr load exception
31535 * @param {Roo.bootstrap.DocumentManager} this
31536 * @param {XMLHttpRequest} xhr
31538 "afterupload" : true,
31541 * prepare the form data
31542 * @param {Roo.bootstrap.DocumentManager} this
31543 * @param {Object} formData
31548 * Fire when remove the file
31549 * @param {Roo.bootstrap.DocumentManager} this
31550 * @param {Object} file
31555 * Fire after refresh the file
31556 * @param {Roo.bootstrap.DocumentManager} this
31561 * Fire after click the image
31562 * @param {Roo.bootstrap.DocumentManager} this
31563 * @param {Object} file
31568 * Fire when upload a image and editable set to true
31569 * @param {Roo.bootstrap.DocumentManager} this
31570 * @param {Object} file
31574 * @event beforeselectfile
31575 * Fire before select file
31576 * @param {Roo.bootstrap.DocumentManager} this
31578 "beforeselectfile" : true,
31581 * Fire before process file
31582 * @param {Roo.bootstrap.DocumentManager} this
31583 * @param {Object} file
31587 * @event previewrendered
31588 * Fire when preview rendered
31589 * @param {Roo.bootstrap.DocumentManager} this
31590 * @param {Object} file
31592 "previewrendered" : true,
31595 "previewResize" : true
31600 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31609 paramName : 'imageUpload',
31610 toolTipName : 'filename',
31613 labelAlign : 'left',
31623 getAutoCreate : function()
31625 var managerWidget = {
31627 cls : 'roo-document-manager',
31631 cls : 'roo-document-manager-selector',
31636 cls : 'roo-document-manager-uploader',
31640 cls : 'roo-document-manager-upload-btn',
31641 html : '<i class="fa fa-plus"></i>'
31652 cls : 'column col-md-12',
31657 if(this.fieldLabel.length){
31662 cls : 'column col-md-12',
31663 html : this.fieldLabel
31667 cls : 'column col-md-12',
31672 if(this.labelAlign == 'left'){
31677 html : this.fieldLabel
31686 if(this.labelWidth > 12){
31687 content[0].style = "width: " + this.labelWidth + 'px';
31690 if(this.labelWidth < 13 && this.labelmd == 0){
31691 this.labelmd = this.labelWidth;
31694 if(this.labellg > 0){
31695 content[0].cls += ' col-lg-' + this.labellg;
31696 content[1].cls += ' col-lg-' + (12 - this.labellg);
31699 if(this.labelmd > 0){
31700 content[0].cls += ' col-md-' + this.labelmd;
31701 content[1].cls += ' col-md-' + (12 - this.labelmd);
31704 if(this.labelsm > 0){
31705 content[0].cls += ' col-sm-' + this.labelsm;
31706 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31709 if(this.labelxs > 0){
31710 content[0].cls += ' col-xs-' + this.labelxs;
31711 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31719 cls : 'row clearfix',
31727 initEvents : function()
31729 this.managerEl = this.el.select('.roo-document-manager', true).first();
31730 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31732 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31733 this.selectorEl.hide();
31736 this.selectorEl.attr('multiple', 'multiple');
31739 this.selectorEl.on('change', this.onFileSelected, this);
31741 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31742 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31744 this.uploader.on('click', this.onUploaderClick, this);
31746 this.renderProgressDialog();
31750 window.addEventListener("resize", function() { _this.refresh(); } );
31752 this.fireEvent('initial', this);
31755 renderProgressDialog : function()
31759 this.progressDialog = new Roo.bootstrap.Modal({
31760 cls : 'roo-document-manager-progress-dialog',
31761 allow_close : false,
31772 btnclick : function() {
31773 _this.uploadCancel();
31779 this.progressDialog.render(Roo.get(document.body));
31781 this.progress = new Roo.bootstrap.Progress({
31782 cls : 'roo-document-manager-progress',
31787 this.progress.render(this.progressDialog.getChildContainer());
31789 this.progressBar = new Roo.bootstrap.ProgressBar({
31790 cls : 'roo-document-manager-progress-bar',
31793 aria_valuemax : 12,
31797 this.progressBar.render(this.progress.getChildContainer());
31800 onUploaderClick : function(e)
31802 e.preventDefault();
31804 if(this.fireEvent('beforeselectfile', this) != false){
31805 this.selectorEl.dom.click();
31810 onFileSelected : function(e)
31812 e.preventDefault();
31814 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31818 Roo.each(this.selectorEl.dom.files, function(file){
31819 if(this.fireEvent('inspect', this, file) != false){
31820 this.files.push(file);
31830 this.selectorEl.dom.value = '';
31832 if(!this.files || !this.files.length){
31836 if(this.boxes > 0 && this.files.length > this.boxes){
31837 this.files = this.files.slice(0, this.boxes);
31840 this.uploader.show();
31842 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31843 this.uploader.hide();
31852 Roo.each(this.files, function(file){
31854 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31855 var f = this.renderPreview(file);
31860 if(file.type.indexOf('image') != -1){
31861 this.delegates.push(
31863 _this.process(file);
31864 }).createDelegate(this)
31872 _this.process(file);
31873 }).createDelegate(this)
31878 this.files = files;
31880 this.delegates = this.delegates.concat(docs);
31882 if(!this.delegates.length){
31887 this.progressBar.aria_valuemax = this.delegates.length;
31894 arrange : function()
31896 if(!this.delegates.length){
31897 this.progressDialog.hide();
31902 var delegate = this.delegates.shift();
31904 this.progressDialog.show();
31906 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31908 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31913 refresh : function()
31915 this.uploader.show();
31917 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31918 this.uploader.hide();
31921 Roo.isTouch ? this.closable(false) : this.closable(true);
31923 this.fireEvent('refresh', this);
31926 onRemove : function(e, el, o)
31928 e.preventDefault();
31930 this.fireEvent('remove', this, o);
31934 remove : function(o)
31938 Roo.each(this.files, function(file){
31939 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31948 this.files = files;
31955 Roo.each(this.files, function(file){
31960 file.target.remove();
31969 onClick : function(e, el, o)
31971 e.preventDefault();
31973 this.fireEvent('click', this, o);
31977 closable : function(closable)
31979 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31981 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31993 xhrOnLoad : function(xhr)
31995 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31999 if (xhr.readyState !== 4) {
32001 this.fireEvent('exception', this, xhr);
32005 var response = Roo.decode(xhr.responseText);
32007 if(!response.success){
32009 this.fireEvent('exception', this, xhr);
32013 var file = this.renderPreview(response.data);
32015 this.files.push(file);
32019 this.fireEvent('afterupload', this, xhr);
32023 xhrOnError : function(xhr)
32025 Roo.log('xhr on error');
32027 var response = Roo.decode(xhr.responseText);
32034 process : function(file)
32036 if(this.fireEvent('process', this, file) !== false){
32037 if(this.editable && file.type.indexOf('image') != -1){
32038 this.fireEvent('edit', this, file);
32042 this.uploadStart(file, false);
32049 uploadStart : function(file, crop)
32051 this.xhr = new XMLHttpRequest();
32053 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32058 file.xhr = this.xhr;
32060 this.managerEl.createChild({
32062 cls : 'roo-document-manager-loading',
32066 tooltip : file.name,
32067 cls : 'roo-document-manager-thumb',
32068 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32074 this.xhr.open(this.method, this.url, true);
32077 "Accept": "application/json",
32078 "Cache-Control": "no-cache",
32079 "X-Requested-With": "XMLHttpRequest"
32082 for (var headerName in headers) {
32083 var headerValue = headers[headerName];
32085 this.xhr.setRequestHeader(headerName, headerValue);
32091 this.xhr.onload = function()
32093 _this.xhrOnLoad(_this.xhr);
32096 this.xhr.onerror = function()
32098 _this.xhrOnError(_this.xhr);
32101 var formData = new FormData();
32103 formData.append('returnHTML', 'NO');
32106 formData.append('crop', crop);
32109 formData.append(this.paramName, file, file.name);
32116 if(this.fireEvent('prepare', this, formData, options) != false){
32118 if(options.manually){
32122 this.xhr.send(formData);
32126 this.uploadCancel();
32129 uploadCancel : function()
32135 this.delegates = [];
32137 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32144 renderPreview : function(file)
32146 if(typeof(file.target) != 'undefined' && file.target){
32150 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32152 var previewEl = this.managerEl.createChild({
32154 cls : 'roo-document-manager-preview',
32158 tooltip : file[this.toolTipName],
32159 cls : 'roo-document-manager-thumb',
32160 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32165 html : '<i class="fa fa-times-circle"></i>'
32170 var close = previewEl.select('button.close', true).first();
32172 close.on('click', this.onRemove, this, file);
32174 file.target = previewEl;
32176 var image = previewEl.select('img', true).first();
32180 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32182 image.on('click', this.onClick, this, file);
32184 this.fireEvent('previewrendered', this, file);
32190 onPreviewLoad : function(file, image)
32192 if(typeof(file.target) == 'undefined' || !file.target){
32196 var width = image.dom.naturalWidth || image.dom.width;
32197 var height = image.dom.naturalHeight || image.dom.height;
32199 if(!this.previewResize) {
32203 if(width > height){
32204 file.target.addClass('wide');
32208 file.target.addClass('tall');
32213 uploadFromSource : function(file, crop)
32215 this.xhr = new XMLHttpRequest();
32217 this.managerEl.createChild({
32219 cls : 'roo-document-manager-loading',
32223 tooltip : file.name,
32224 cls : 'roo-document-manager-thumb',
32225 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32231 this.xhr.open(this.method, this.url, true);
32234 "Accept": "application/json",
32235 "Cache-Control": "no-cache",
32236 "X-Requested-With": "XMLHttpRequest"
32239 for (var headerName in headers) {
32240 var headerValue = headers[headerName];
32242 this.xhr.setRequestHeader(headerName, headerValue);
32248 this.xhr.onload = function()
32250 _this.xhrOnLoad(_this.xhr);
32253 this.xhr.onerror = function()
32255 _this.xhrOnError(_this.xhr);
32258 var formData = new FormData();
32260 formData.append('returnHTML', 'NO');
32262 formData.append('crop', crop);
32264 if(typeof(file.filename) != 'undefined'){
32265 formData.append('filename', file.filename);
32268 if(typeof(file.mimetype) != 'undefined'){
32269 formData.append('mimetype', file.mimetype);
32274 if(this.fireEvent('prepare', this, formData) != false){
32275 this.xhr.send(formData);
32285 * @class Roo.bootstrap.DocumentViewer
32286 * @extends Roo.bootstrap.Component
32287 * Bootstrap DocumentViewer class
32288 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32289 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32292 * Create a new DocumentViewer
32293 * @param {Object} config The config object
32296 Roo.bootstrap.DocumentViewer = function(config){
32297 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32302 * Fire after initEvent
32303 * @param {Roo.bootstrap.DocumentViewer} this
32309 * @param {Roo.bootstrap.DocumentViewer} this
32314 * Fire after download button
32315 * @param {Roo.bootstrap.DocumentViewer} this
32320 * Fire after trash button
32321 * @param {Roo.bootstrap.DocumentViewer} this
32328 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32330 showDownload : true,
32334 getAutoCreate : function()
32338 cls : 'roo-document-viewer',
32342 cls : 'roo-document-viewer-body',
32346 cls : 'roo-document-viewer-thumb',
32350 cls : 'roo-document-viewer-image'
32358 cls : 'roo-document-viewer-footer',
32361 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32365 cls : 'btn-group roo-document-viewer-download',
32369 cls : 'btn btn-default',
32370 html : '<i class="fa fa-download"></i>'
32376 cls : 'btn-group roo-document-viewer-trash',
32380 cls : 'btn btn-default',
32381 html : '<i class="fa fa-trash"></i>'
32394 initEvents : function()
32396 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32397 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32399 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32400 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32402 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32403 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32405 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32406 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32408 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32409 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32411 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32412 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32414 this.bodyEl.on('click', this.onClick, this);
32415 this.downloadBtn.on('click', this.onDownload, this);
32416 this.trashBtn.on('click', this.onTrash, this);
32418 this.downloadBtn.hide();
32419 this.trashBtn.hide();
32421 if(this.showDownload){
32422 this.downloadBtn.show();
32425 if(this.showTrash){
32426 this.trashBtn.show();
32429 if(!this.showDownload && !this.showTrash) {
32430 this.footerEl.hide();
32435 initial : function()
32437 this.fireEvent('initial', this);
32441 onClick : function(e)
32443 e.preventDefault();
32445 this.fireEvent('click', this);
32448 onDownload : function(e)
32450 e.preventDefault();
32452 this.fireEvent('download', this);
32455 onTrash : function(e)
32457 e.preventDefault();
32459 this.fireEvent('trash', this);
32471 * @class Roo.bootstrap.NavProgressBar
32472 * @extends Roo.bootstrap.Component
32473 * Bootstrap NavProgressBar class
32476 * Create a new nav progress bar
32477 * @param {Object} config The config object
32480 Roo.bootstrap.NavProgressBar = function(config){
32481 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32483 this.bullets = this.bullets || [];
32485 // Roo.bootstrap.NavProgressBar.register(this);
32489 * Fires when the active item changes
32490 * @param {Roo.bootstrap.NavProgressBar} this
32491 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32492 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32499 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32504 getAutoCreate : function()
32506 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32510 cls : 'roo-navigation-bar-group',
32514 cls : 'roo-navigation-top-bar'
32518 cls : 'roo-navigation-bullets-bar',
32522 cls : 'roo-navigation-bar'
32529 cls : 'roo-navigation-bottom-bar'
32539 initEvents: function()
32544 onRender : function(ct, position)
32546 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32548 if(this.bullets.length){
32549 Roo.each(this.bullets, function(b){
32558 addItem : function(cfg)
32560 var item = new Roo.bootstrap.NavProgressItem(cfg);
32562 item.parentId = this.id;
32563 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32566 var top = new Roo.bootstrap.Element({
32568 cls : 'roo-navigation-bar-text'
32571 var bottom = new Roo.bootstrap.Element({
32573 cls : 'roo-navigation-bar-text'
32576 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32577 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32579 var topText = new Roo.bootstrap.Element({
32581 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32584 var bottomText = new Roo.bootstrap.Element({
32586 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32589 topText.onRender(top.el, null);
32590 bottomText.onRender(bottom.el, null);
32593 item.bottomEl = bottom;
32596 this.barItems.push(item);
32601 getActive : function()
32603 var active = false;
32605 Roo.each(this.barItems, function(v){
32607 if (!v.isActive()) {
32619 setActiveItem : function(item)
32623 Roo.each(this.barItems, function(v){
32624 if (v.rid == item.rid) {
32628 if (v.isActive()) {
32629 v.setActive(false);
32634 item.setActive(true);
32636 this.fireEvent('changed', this, item, prev);
32639 getBarItem: function(rid)
32643 Roo.each(this.barItems, function(e) {
32644 if (e.rid != rid) {
32655 indexOfItem : function(item)
32659 Roo.each(this.barItems, function(v, i){
32661 if (v.rid != item.rid) {
32672 setActiveNext : function()
32674 var i = this.indexOfItem(this.getActive());
32676 if (i > this.barItems.length) {
32680 this.setActiveItem(this.barItems[i+1]);
32683 setActivePrev : function()
32685 var i = this.indexOfItem(this.getActive());
32691 this.setActiveItem(this.barItems[i-1]);
32694 format : function()
32696 if(!this.barItems.length){
32700 var width = 100 / this.barItems.length;
32702 Roo.each(this.barItems, function(i){
32703 i.el.setStyle('width', width + '%');
32704 i.topEl.el.setStyle('width', width + '%');
32705 i.bottomEl.el.setStyle('width', width + '%');
32714 * Nav Progress Item
32719 * @class Roo.bootstrap.NavProgressItem
32720 * @extends Roo.bootstrap.Component
32721 * Bootstrap NavProgressItem class
32722 * @cfg {String} rid the reference id
32723 * @cfg {Boolean} active (true|false) Is item active default false
32724 * @cfg {Boolean} disabled (true|false) Is item active default false
32725 * @cfg {String} html
32726 * @cfg {String} position (top|bottom) text position default bottom
32727 * @cfg {String} icon show icon instead of number
32730 * Create a new NavProgressItem
32731 * @param {Object} config The config object
32733 Roo.bootstrap.NavProgressItem = function(config){
32734 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32739 * The raw click event for the entire grid.
32740 * @param {Roo.bootstrap.NavProgressItem} this
32741 * @param {Roo.EventObject} e
32748 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32754 position : 'bottom',
32757 getAutoCreate : function()
32759 var iconCls = 'roo-navigation-bar-item-icon';
32761 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32765 cls: 'roo-navigation-bar-item',
32775 cfg.cls += ' active';
32778 cfg.cls += ' disabled';
32784 disable : function()
32786 this.setDisabled(true);
32789 enable : function()
32791 this.setDisabled(false);
32794 initEvents: function()
32796 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32798 this.iconEl.on('click', this.onClick, this);
32801 onClick : function(e)
32803 e.preventDefault();
32809 if(this.fireEvent('click', this, e) === false){
32813 this.parent().setActiveItem(this);
32816 isActive: function ()
32818 return this.active;
32821 setActive : function(state)
32823 if(this.active == state){
32827 this.active = state;
32830 this.el.addClass('active');
32834 this.el.removeClass('active');
32839 setDisabled : function(state)
32841 if(this.disabled == state){
32845 this.disabled = state;
32848 this.el.addClass('disabled');
32852 this.el.removeClass('disabled');
32855 tooltipEl : function()
32857 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32870 * @class Roo.bootstrap.FieldLabel
32871 * @extends Roo.bootstrap.Component
32872 * Bootstrap FieldLabel class
32873 * @cfg {String} html contents of the element
32874 * @cfg {String} tag tag of the element default label
32875 * @cfg {String} cls class of the element
32876 * @cfg {String} target label target
32877 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32878 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32879 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32880 * @cfg {String} iconTooltip default "This field is required"
32881 * @cfg {String} indicatorpos (left|right) default left
32884 * Create a new FieldLabel
32885 * @param {Object} config The config object
32888 Roo.bootstrap.FieldLabel = function(config){
32889 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32894 * Fires after the field has been marked as invalid.
32895 * @param {Roo.form.FieldLabel} this
32896 * @param {String} msg The validation message
32901 * Fires after the field has been validated with no errors.
32902 * @param {Roo.form.FieldLabel} this
32908 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32915 invalidClass : 'has-warning',
32916 validClass : 'has-success',
32917 iconTooltip : 'This field is required',
32918 indicatorpos : 'left',
32920 getAutoCreate : function(){
32923 if (!this.allowBlank) {
32929 cls : 'roo-bootstrap-field-label ' + this.cls,
32934 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32935 tooltip : this.iconTooltip
32944 if(this.indicatorpos == 'right'){
32947 cls : 'roo-bootstrap-field-label ' + this.cls,
32956 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32957 tooltip : this.iconTooltip
32966 initEvents: function()
32968 Roo.bootstrap.Element.superclass.initEvents.call(this);
32970 this.indicator = this.indicatorEl();
32972 if(this.indicator){
32973 this.indicator.removeClass('visible');
32974 this.indicator.addClass('invisible');
32977 Roo.bootstrap.FieldLabel.register(this);
32980 indicatorEl : function()
32982 var indicator = this.el.select('i.roo-required-indicator',true).first();
32993 * Mark this field as valid
32995 markValid : function()
32997 if(this.indicator){
32998 this.indicator.removeClass('visible');
32999 this.indicator.addClass('invisible');
33001 if (Roo.bootstrap.version == 3) {
33002 this.el.removeClass(this.invalidClass);
33003 this.el.addClass(this.validClass);
33005 this.el.removeClass('is-invalid');
33006 this.el.addClass('is-valid');
33010 this.fireEvent('valid', this);
33014 * Mark this field as invalid
33015 * @param {String} msg The validation message
33017 markInvalid : function(msg)
33019 if(this.indicator){
33020 this.indicator.removeClass('invisible');
33021 this.indicator.addClass('visible');
33023 if (Roo.bootstrap.version == 3) {
33024 this.el.removeClass(this.validClass);
33025 this.el.addClass(this.invalidClass);
33027 this.el.removeClass('is-valid');
33028 this.el.addClass('is-invalid');
33032 this.fireEvent('invalid', this, msg);
33038 Roo.apply(Roo.bootstrap.FieldLabel, {
33043 * register a FieldLabel Group
33044 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33046 register : function(label)
33048 if(this.groups.hasOwnProperty(label.target)){
33052 this.groups[label.target] = label;
33056 * fetch a FieldLabel Group based on the target
33057 * @param {string} target
33058 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33060 get: function(target) {
33061 if (typeof(this.groups[target]) == 'undefined') {
33065 return this.groups[target] ;
33074 * page DateSplitField.
33080 * @class Roo.bootstrap.DateSplitField
33081 * @extends Roo.bootstrap.Component
33082 * Bootstrap DateSplitField class
33083 * @cfg {string} fieldLabel - the label associated
33084 * @cfg {Number} labelWidth set the width of label (0-12)
33085 * @cfg {String} labelAlign (top|left)
33086 * @cfg {Boolean} dayAllowBlank (true|false) default false
33087 * @cfg {Boolean} monthAllowBlank (true|false) default false
33088 * @cfg {Boolean} yearAllowBlank (true|false) default false
33089 * @cfg {string} dayPlaceholder
33090 * @cfg {string} monthPlaceholder
33091 * @cfg {string} yearPlaceholder
33092 * @cfg {string} dayFormat default 'd'
33093 * @cfg {string} monthFormat default 'm'
33094 * @cfg {string} yearFormat default 'Y'
33095 * @cfg {Number} labellg set the width of label (1-12)
33096 * @cfg {Number} labelmd set the width of label (1-12)
33097 * @cfg {Number} labelsm set the width of label (1-12)
33098 * @cfg {Number} labelxs set the width of label (1-12)
33102 * Create a new DateSplitField
33103 * @param {Object} config The config object
33106 Roo.bootstrap.DateSplitField = function(config){
33107 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33113 * getting the data of years
33114 * @param {Roo.bootstrap.DateSplitField} this
33115 * @param {Object} years
33120 * getting the data of days
33121 * @param {Roo.bootstrap.DateSplitField} this
33122 * @param {Object} days
33127 * Fires after the field has been marked as invalid.
33128 * @param {Roo.form.Field} this
33129 * @param {String} msg The validation message
33134 * Fires after the field has been validated with no errors.
33135 * @param {Roo.form.Field} this
33141 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33144 labelAlign : 'top',
33146 dayAllowBlank : false,
33147 monthAllowBlank : false,
33148 yearAllowBlank : false,
33149 dayPlaceholder : '',
33150 monthPlaceholder : '',
33151 yearPlaceholder : '',
33155 isFormField : true,
33161 getAutoCreate : function()
33165 cls : 'row roo-date-split-field-group',
33170 cls : 'form-hidden-field roo-date-split-field-group-value',
33176 var labelCls = 'col-md-12';
33177 var contentCls = 'col-md-4';
33179 if(this.fieldLabel){
33183 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33187 html : this.fieldLabel
33192 if(this.labelAlign == 'left'){
33194 if(this.labelWidth > 12){
33195 label.style = "width: " + this.labelWidth + 'px';
33198 if(this.labelWidth < 13 && this.labelmd == 0){
33199 this.labelmd = this.labelWidth;
33202 if(this.labellg > 0){
33203 labelCls = ' col-lg-' + this.labellg;
33204 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33207 if(this.labelmd > 0){
33208 labelCls = ' col-md-' + this.labelmd;
33209 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33212 if(this.labelsm > 0){
33213 labelCls = ' col-sm-' + this.labelsm;
33214 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33217 if(this.labelxs > 0){
33218 labelCls = ' col-xs-' + this.labelxs;
33219 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33223 label.cls += ' ' + labelCls;
33225 cfg.cn.push(label);
33228 Roo.each(['day', 'month', 'year'], function(t){
33231 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33238 inputEl: function ()
33240 return this.el.select('.roo-date-split-field-group-value', true).first();
33243 onRender : function(ct, position)
33247 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33249 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33251 this.dayField = new Roo.bootstrap.ComboBox({
33252 allowBlank : this.dayAllowBlank,
33253 alwaysQuery : true,
33254 displayField : 'value',
33257 forceSelection : true,
33259 placeholder : this.dayPlaceholder,
33260 selectOnFocus : true,
33261 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33262 triggerAction : 'all',
33264 valueField : 'value',
33265 store : new Roo.data.SimpleStore({
33266 data : (function() {
33268 _this.fireEvent('days', _this, days);
33271 fields : [ 'value' ]
33274 select : function (_self, record, index)
33276 _this.setValue(_this.getValue());
33281 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33283 this.monthField = new Roo.bootstrap.MonthField({
33284 after : '<i class=\"fa fa-calendar\"></i>',
33285 allowBlank : this.monthAllowBlank,
33286 placeholder : this.monthPlaceholder,
33289 render : function (_self)
33291 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33292 e.preventDefault();
33296 select : function (_self, oldvalue, newvalue)
33298 _this.setValue(_this.getValue());
33303 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33305 this.yearField = new Roo.bootstrap.ComboBox({
33306 allowBlank : this.yearAllowBlank,
33307 alwaysQuery : true,
33308 displayField : 'value',
33311 forceSelection : true,
33313 placeholder : this.yearPlaceholder,
33314 selectOnFocus : true,
33315 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33316 triggerAction : 'all',
33318 valueField : 'value',
33319 store : new Roo.data.SimpleStore({
33320 data : (function() {
33322 _this.fireEvent('years', _this, years);
33325 fields : [ 'value' ]
33328 select : function (_self, record, index)
33330 _this.setValue(_this.getValue());
33335 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33338 setValue : function(v, format)
33340 this.inputEl.dom.value = v;
33342 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33344 var d = Date.parseDate(v, f);
33351 this.setDay(d.format(this.dayFormat));
33352 this.setMonth(d.format(this.monthFormat));
33353 this.setYear(d.format(this.yearFormat));
33360 setDay : function(v)
33362 this.dayField.setValue(v);
33363 this.inputEl.dom.value = this.getValue();
33368 setMonth : function(v)
33370 this.monthField.setValue(v, true);
33371 this.inputEl.dom.value = this.getValue();
33376 setYear : function(v)
33378 this.yearField.setValue(v);
33379 this.inputEl.dom.value = this.getValue();
33384 getDay : function()
33386 return this.dayField.getValue();
33389 getMonth : function()
33391 return this.monthField.getValue();
33394 getYear : function()
33396 return this.yearField.getValue();
33399 getValue : function()
33401 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33403 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33413 this.inputEl.dom.value = '';
33418 validate : function()
33420 var d = this.dayField.validate();
33421 var m = this.monthField.validate();
33422 var y = this.yearField.validate();
33427 (!this.dayAllowBlank && !d) ||
33428 (!this.monthAllowBlank && !m) ||
33429 (!this.yearAllowBlank && !y)
33434 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33443 this.markInvalid();
33448 markValid : function()
33451 var label = this.el.select('label', true).first();
33452 var icon = this.el.select('i.fa-star', true).first();
33458 this.fireEvent('valid', this);
33462 * Mark this field as invalid
33463 * @param {String} msg The validation message
33465 markInvalid : function(msg)
33468 var label = this.el.select('label', true).first();
33469 var icon = this.el.select('i.fa-star', true).first();
33471 if(label && !icon){
33472 this.el.select('.roo-date-split-field-label', true).createChild({
33474 cls : 'text-danger fa fa-lg fa-star',
33475 tooltip : 'This field is required',
33476 style : 'margin-right:5px;'
33480 this.fireEvent('invalid', this, msg);
33483 clearInvalid : function()
33485 var label = this.el.select('label', true).first();
33486 var icon = this.el.select('i.fa-star', true).first();
33492 this.fireEvent('valid', this);
33495 getName: function()
33505 * http://masonry.desandro.com
33507 * The idea is to render all the bricks based on vertical width...
33509 * The original code extends 'outlayer' - we might need to use that....
33515 * @class Roo.bootstrap.LayoutMasonry
33516 * @extends Roo.bootstrap.Component
33517 * Bootstrap Layout Masonry class
33520 * Create a new Element
33521 * @param {Object} config The config object
33524 Roo.bootstrap.LayoutMasonry = function(config){
33526 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33530 Roo.bootstrap.LayoutMasonry.register(this);
33536 * Fire after layout the items
33537 * @param {Roo.bootstrap.LayoutMasonry} this
33538 * @param {Roo.EventObject} e
33545 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33548 * @cfg {Boolean} isLayoutInstant = no animation?
33550 isLayoutInstant : false, // needed?
33553 * @cfg {Number} boxWidth width of the columns
33558 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33563 * @cfg {Number} padWidth padding below box..
33568 * @cfg {Number} gutter gutter width..
33573 * @cfg {Number} maxCols maximum number of columns
33579 * @cfg {Boolean} isAutoInitial defalut true
33581 isAutoInitial : true,
33586 * @cfg {Boolean} isHorizontal defalut false
33588 isHorizontal : false,
33590 currentSize : null,
33596 bricks: null, //CompositeElement
33600 _isLayoutInited : false,
33602 // isAlternative : false, // only use for vertical layout...
33605 * @cfg {Number} alternativePadWidth padding below box..
33607 alternativePadWidth : 50,
33609 selectedBrick : [],
33611 getAutoCreate : function(){
33613 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33617 cls: 'blog-masonary-wrapper ' + this.cls,
33619 cls : 'mas-boxes masonary'
33626 getChildContainer: function( )
33628 if (this.boxesEl) {
33629 return this.boxesEl;
33632 this.boxesEl = this.el.select('.mas-boxes').first();
33634 return this.boxesEl;
33638 initEvents : function()
33642 if(this.isAutoInitial){
33643 Roo.log('hook children rendered');
33644 this.on('childrenrendered', function() {
33645 Roo.log('children rendered');
33651 initial : function()
33653 this.selectedBrick = [];
33655 this.currentSize = this.el.getBox(true);
33657 Roo.EventManager.onWindowResize(this.resize, this);
33659 if(!this.isAutoInitial){
33667 //this.layout.defer(500,this);
33671 resize : function()
33673 var cs = this.el.getBox(true);
33676 this.currentSize.width == cs.width &&
33677 this.currentSize.x == cs.x &&
33678 this.currentSize.height == cs.height &&
33679 this.currentSize.y == cs.y
33681 Roo.log("no change in with or X or Y");
33685 this.currentSize = cs;
33691 layout : function()
33693 this._resetLayout();
33695 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33697 this.layoutItems( isInstant );
33699 this._isLayoutInited = true;
33701 this.fireEvent('layout', this);
33705 _resetLayout : function()
33707 if(this.isHorizontal){
33708 this.horizontalMeasureColumns();
33712 this.verticalMeasureColumns();
33716 verticalMeasureColumns : function()
33718 this.getContainerWidth();
33720 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33721 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33725 var boxWidth = this.boxWidth + this.padWidth;
33727 if(this.containerWidth < this.boxWidth){
33728 boxWidth = this.containerWidth
33731 var containerWidth = this.containerWidth;
33733 var cols = Math.floor(containerWidth / boxWidth);
33735 this.cols = Math.max( cols, 1 );
33737 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33739 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33741 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33743 this.colWidth = boxWidth + avail - this.padWidth;
33745 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33746 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33749 horizontalMeasureColumns : function()
33751 this.getContainerWidth();
33753 var boxWidth = this.boxWidth;
33755 if(this.containerWidth < boxWidth){
33756 boxWidth = this.containerWidth;
33759 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33761 this.el.setHeight(boxWidth);
33765 getContainerWidth : function()
33767 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33770 layoutItems : function( isInstant )
33772 Roo.log(this.bricks);
33774 var items = Roo.apply([], this.bricks);
33776 if(this.isHorizontal){
33777 this._horizontalLayoutItems( items , isInstant );
33781 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33782 // this._verticalAlternativeLayoutItems( items , isInstant );
33786 this._verticalLayoutItems( items , isInstant );
33790 _verticalLayoutItems : function ( items , isInstant)
33792 if ( !items || !items.length ) {
33797 ['xs', 'xs', 'xs', 'tall'],
33798 ['xs', 'xs', 'tall'],
33799 ['xs', 'xs', 'sm'],
33800 ['xs', 'xs', 'xs'],
33806 ['sm', 'xs', 'xs'],
33810 ['tall', 'xs', 'xs', 'xs'],
33811 ['tall', 'xs', 'xs'],
33823 Roo.each(items, function(item, k){
33825 switch (item.size) {
33826 // these layouts take up a full box,
33837 boxes.push([item]);
33860 var filterPattern = function(box, length)
33868 var pattern = box.slice(0, length);
33872 Roo.each(pattern, function(i){
33873 format.push(i.size);
33876 Roo.each(standard, function(s){
33878 if(String(s) != String(format)){
33887 if(!match && length == 1){
33892 filterPattern(box, length - 1);
33896 queue.push(pattern);
33898 box = box.slice(length, box.length);
33900 filterPattern(box, 4);
33906 Roo.each(boxes, function(box, k){
33912 if(box.length == 1){
33917 filterPattern(box, 4);
33921 this._processVerticalLayoutQueue( queue, isInstant );
33925 // _verticalAlternativeLayoutItems : function( items , isInstant )
33927 // if ( !items || !items.length ) {
33931 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33935 _horizontalLayoutItems : function ( items , isInstant)
33937 if ( !items || !items.length || items.length < 3) {
33943 var eItems = items.slice(0, 3);
33945 items = items.slice(3, items.length);
33948 ['xs', 'xs', 'xs', 'wide'],
33949 ['xs', 'xs', 'wide'],
33950 ['xs', 'xs', 'sm'],
33951 ['xs', 'xs', 'xs'],
33957 ['sm', 'xs', 'xs'],
33961 ['wide', 'xs', 'xs', 'xs'],
33962 ['wide', 'xs', 'xs'],
33975 Roo.each(items, function(item, k){
33977 switch (item.size) {
33988 boxes.push([item]);
34012 var filterPattern = function(box, length)
34020 var pattern = box.slice(0, length);
34024 Roo.each(pattern, function(i){
34025 format.push(i.size);
34028 Roo.each(standard, function(s){
34030 if(String(s) != String(format)){
34039 if(!match && length == 1){
34044 filterPattern(box, length - 1);
34048 queue.push(pattern);
34050 box = box.slice(length, box.length);
34052 filterPattern(box, 4);
34058 Roo.each(boxes, function(box, k){
34064 if(box.length == 1){
34069 filterPattern(box, 4);
34076 var pos = this.el.getBox(true);
34080 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34082 var hit_end = false;
34084 Roo.each(queue, function(box){
34088 Roo.each(box, function(b){
34090 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34100 Roo.each(box, function(b){
34102 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34105 mx = Math.max(mx, b.x);
34109 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34113 Roo.each(box, function(b){
34115 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34129 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34132 /** Sets position of item in DOM
34133 * @param {Element} item
34134 * @param {Number} x - horizontal position
34135 * @param {Number} y - vertical position
34136 * @param {Boolean} isInstant - disables transitions
34138 _processVerticalLayoutQueue : function( queue, isInstant )
34140 var pos = this.el.getBox(true);
34145 for (var i = 0; i < this.cols; i++){
34149 Roo.each(queue, function(box, k){
34151 var col = k % this.cols;
34153 Roo.each(box, function(b,kk){
34155 b.el.position('absolute');
34157 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34158 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34160 if(b.size == 'md-left' || b.size == 'md-right'){
34161 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34162 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34165 b.el.setWidth(width);
34166 b.el.setHeight(height);
34168 b.el.select('iframe',true).setSize(width,height);
34172 for (var i = 0; i < this.cols; i++){
34174 if(maxY[i] < maxY[col]){
34179 col = Math.min(col, i);
34183 x = pos.x + col * (this.colWidth + this.padWidth);
34187 var positions = [];
34189 switch (box.length){
34191 positions = this.getVerticalOneBoxColPositions(x, y, box);
34194 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34197 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34200 positions = this.getVerticalFourBoxColPositions(x, y, box);
34206 Roo.each(box, function(b,kk){
34208 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34210 var sz = b.el.getSize();
34212 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34220 for (var i = 0; i < this.cols; i++){
34221 mY = Math.max(mY, maxY[i]);
34224 this.el.setHeight(mY - pos.y);
34228 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34230 // var pos = this.el.getBox(true);
34233 // var maxX = pos.right;
34235 // var maxHeight = 0;
34237 // Roo.each(items, function(item, k){
34241 // item.el.position('absolute');
34243 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34245 // item.el.setWidth(width);
34247 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34249 // item.el.setHeight(height);
34252 // item.el.setXY([x, y], isInstant ? false : true);
34254 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34257 // y = y + height + this.alternativePadWidth;
34259 // maxHeight = maxHeight + height + this.alternativePadWidth;
34263 // this.el.setHeight(maxHeight);
34267 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34269 var pos = this.el.getBox(true);
34274 var maxX = pos.right;
34276 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34278 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34280 Roo.each(queue, function(box, k){
34282 Roo.each(box, function(b, kk){
34284 b.el.position('absolute');
34286 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34287 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34289 if(b.size == 'md-left' || b.size == 'md-right'){
34290 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34291 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34294 b.el.setWidth(width);
34295 b.el.setHeight(height);
34303 var positions = [];
34305 switch (box.length){
34307 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34310 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34313 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34316 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34322 Roo.each(box, function(b,kk){
34324 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34326 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34334 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34336 Roo.each(eItems, function(b,k){
34338 b.size = (k == 0) ? 'sm' : 'xs';
34339 b.x = (k == 0) ? 2 : 1;
34340 b.y = (k == 0) ? 2 : 1;
34342 b.el.position('absolute');
34344 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34346 b.el.setWidth(width);
34348 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34350 b.el.setHeight(height);
34354 var positions = [];
34357 x : maxX - this.unitWidth * 2 - this.gutter,
34362 x : maxX - this.unitWidth,
34363 y : minY + (this.unitWidth + this.gutter) * 2
34367 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34371 Roo.each(eItems, function(b,k){
34373 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34379 getVerticalOneBoxColPositions : function(x, y, box)
34383 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34385 if(box[0].size == 'md-left'){
34389 if(box[0].size == 'md-right'){
34394 x : x + (this.unitWidth + this.gutter) * rand,
34401 getVerticalTwoBoxColPositions : function(x, y, box)
34405 if(box[0].size == 'xs'){
34409 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34413 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34427 x : x + (this.unitWidth + this.gutter) * 2,
34428 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34435 getVerticalThreeBoxColPositions : function(x, y, box)
34439 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34447 x : x + (this.unitWidth + this.gutter) * 1,
34452 x : x + (this.unitWidth + this.gutter) * 2,
34460 if(box[0].size == 'xs' && box[1].size == 'xs'){
34469 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34473 x : x + (this.unitWidth + this.gutter) * 1,
34487 x : x + (this.unitWidth + this.gutter) * 2,
34492 x : x + (this.unitWidth + this.gutter) * 2,
34493 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34500 getVerticalFourBoxColPositions : function(x, y, box)
34504 if(box[0].size == 'xs'){
34513 y : y + (this.unitHeight + this.gutter) * 1
34518 y : y + (this.unitHeight + this.gutter) * 2
34522 x : x + (this.unitWidth + this.gutter) * 1,
34536 x : x + (this.unitWidth + this.gutter) * 2,
34541 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34542 y : y + (this.unitHeight + this.gutter) * 1
34546 x : x + (this.unitWidth + this.gutter) * 2,
34547 y : y + (this.unitWidth + this.gutter) * 2
34554 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34558 if(box[0].size == 'md-left'){
34560 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34567 if(box[0].size == 'md-right'){
34569 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34570 y : minY + (this.unitWidth + this.gutter) * 1
34576 var rand = Math.floor(Math.random() * (4 - box[0].y));
34579 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34580 y : minY + (this.unitWidth + this.gutter) * rand
34587 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34591 if(box[0].size == 'xs'){
34594 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34599 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34600 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34608 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34613 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34614 y : minY + (this.unitWidth + this.gutter) * 2
34621 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34625 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34628 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34633 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34634 y : minY + (this.unitWidth + this.gutter) * 1
34638 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34639 y : minY + (this.unitWidth + this.gutter) * 2
34646 if(box[0].size == 'xs' && box[1].size == 'xs'){
34649 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34654 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34659 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34660 y : minY + (this.unitWidth + this.gutter) * 1
34668 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34673 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34674 y : minY + (this.unitWidth + this.gutter) * 2
34678 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34679 y : minY + (this.unitWidth + this.gutter) * 2
34686 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34690 if(box[0].size == 'xs'){
34693 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34698 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34703 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),
34708 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34709 y : minY + (this.unitWidth + this.gutter) * 1
34717 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34722 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34723 y : minY + (this.unitWidth + this.gutter) * 2
34727 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34728 y : minY + (this.unitWidth + this.gutter) * 2
34732 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),
34733 y : minY + (this.unitWidth + this.gutter) * 2
34741 * remove a Masonry Brick
34742 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34744 removeBrick : function(brick_id)
34750 for (var i = 0; i<this.bricks.length; i++) {
34751 if (this.bricks[i].id == brick_id) {
34752 this.bricks.splice(i,1);
34753 this.el.dom.removeChild(Roo.get(brick_id).dom);
34760 * adds a Masonry Brick
34761 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34763 addBrick : function(cfg)
34765 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34766 //this.register(cn);
34767 cn.parentId = this.id;
34768 cn.render(this.el);
34773 * register a Masonry Brick
34774 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34777 register : function(brick)
34779 this.bricks.push(brick);
34780 brick.masonryId = this.id;
34784 * clear all the Masonry Brick
34786 clearAll : function()
34789 //this.getChildContainer().dom.innerHTML = "";
34790 this.el.dom.innerHTML = '';
34793 getSelected : function()
34795 if (!this.selectedBrick) {
34799 return this.selectedBrick;
34803 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34807 * register a Masonry Layout
34808 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34811 register : function(layout)
34813 this.groups[layout.id] = layout;
34816 * fetch a Masonry Layout based on the masonry layout ID
34817 * @param {string} the masonry layout to add
34818 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34821 get: function(layout_id) {
34822 if (typeof(this.groups[layout_id]) == 'undefined') {
34825 return this.groups[layout_id] ;
34837 * http://masonry.desandro.com
34839 * The idea is to render all the bricks based on vertical width...
34841 * The original code extends 'outlayer' - we might need to use that....
34847 * @class Roo.bootstrap.LayoutMasonryAuto
34848 * @extends Roo.bootstrap.Component
34849 * Bootstrap Layout Masonry class
34852 * Create a new Element
34853 * @param {Object} config The config object
34856 Roo.bootstrap.LayoutMasonryAuto = function(config){
34857 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34860 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34863 * @cfg {Boolean} isFitWidth - resize the width..
34865 isFitWidth : false, // options..
34867 * @cfg {Boolean} isOriginLeft = left align?
34869 isOriginLeft : true,
34871 * @cfg {Boolean} isOriginTop = top align?
34873 isOriginTop : false,
34875 * @cfg {Boolean} isLayoutInstant = no animation?
34877 isLayoutInstant : false, // needed?
34879 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34881 isResizingContainer : true,
34883 * @cfg {Number} columnWidth width of the columns
34889 * @cfg {Number} maxCols maximum number of columns
34894 * @cfg {Number} padHeight padding below box..
34900 * @cfg {Boolean} isAutoInitial defalut true
34903 isAutoInitial : true,
34909 initialColumnWidth : 0,
34910 currentSize : null,
34912 colYs : null, // array.
34919 bricks: null, //CompositeElement
34920 cols : 0, // array?
34921 // element : null, // wrapped now this.el
34922 _isLayoutInited : null,
34925 getAutoCreate : function(){
34929 cls: 'blog-masonary-wrapper ' + this.cls,
34931 cls : 'mas-boxes masonary'
34938 getChildContainer: function( )
34940 if (this.boxesEl) {
34941 return this.boxesEl;
34944 this.boxesEl = this.el.select('.mas-boxes').first();
34946 return this.boxesEl;
34950 initEvents : function()
34954 if(this.isAutoInitial){
34955 Roo.log('hook children rendered');
34956 this.on('childrenrendered', function() {
34957 Roo.log('children rendered');
34964 initial : function()
34966 this.reloadItems();
34968 this.currentSize = this.el.getBox(true);
34970 /// was window resize... - let's see if this works..
34971 Roo.EventManager.onWindowResize(this.resize, this);
34973 if(!this.isAutoInitial){
34978 this.layout.defer(500,this);
34981 reloadItems: function()
34983 this.bricks = this.el.select('.masonry-brick', true);
34985 this.bricks.each(function(b) {
34986 //Roo.log(b.getSize());
34987 if (!b.attr('originalwidth')) {
34988 b.attr('originalwidth', b.getSize().width);
34993 Roo.log(this.bricks.elements.length);
34996 resize : function()
34999 var cs = this.el.getBox(true);
35001 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35002 Roo.log("no change in with or X");
35005 this.currentSize = cs;
35009 layout : function()
35012 this._resetLayout();
35013 //this._manageStamps();
35015 // don't animate first layout
35016 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35017 this.layoutItems( isInstant );
35019 // flag for initalized
35020 this._isLayoutInited = true;
35023 layoutItems : function( isInstant )
35025 //var items = this._getItemsForLayout( this.items );
35026 // original code supports filtering layout items.. we just ignore it..
35028 this._layoutItems( this.bricks , isInstant );
35030 this._postLayout();
35032 _layoutItems : function ( items , isInstant)
35034 //this.fireEvent( 'layout', this, items );
35037 if ( !items || !items.elements.length ) {
35038 // no items, emit event with empty array
35043 items.each(function(item) {
35044 Roo.log("layout item");
35046 // get x/y object from method
35047 var position = this._getItemLayoutPosition( item );
35049 position.item = item;
35050 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35051 queue.push( position );
35054 this._processLayoutQueue( queue );
35056 /** Sets position of item in DOM
35057 * @param {Element} item
35058 * @param {Number} x - horizontal position
35059 * @param {Number} y - vertical position
35060 * @param {Boolean} isInstant - disables transitions
35062 _processLayoutQueue : function( queue )
35064 for ( var i=0, len = queue.length; i < len; i++ ) {
35065 var obj = queue[i];
35066 obj.item.position('absolute');
35067 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35073 * Any logic you want to do after each layout,
35074 * i.e. size the container
35076 _postLayout : function()
35078 this.resizeContainer();
35081 resizeContainer : function()
35083 if ( !this.isResizingContainer ) {
35086 var size = this._getContainerSize();
35088 this.el.setSize(size.width,size.height);
35089 this.boxesEl.setSize(size.width,size.height);
35095 _resetLayout : function()
35097 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35098 this.colWidth = this.el.getWidth();
35099 //this.gutter = this.el.getWidth();
35101 this.measureColumns();
35107 this.colYs.push( 0 );
35113 measureColumns : function()
35115 this.getContainerWidth();
35116 // if columnWidth is 0, default to outerWidth of first item
35117 if ( !this.columnWidth ) {
35118 var firstItem = this.bricks.first();
35119 Roo.log(firstItem);
35120 this.columnWidth = this.containerWidth;
35121 if (firstItem && firstItem.attr('originalwidth') ) {
35122 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35124 // columnWidth fall back to item of first element
35125 Roo.log("set column width?");
35126 this.initialColumnWidth = this.columnWidth ;
35128 // if first elem has no width, default to size of container
35133 if (this.initialColumnWidth) {
35134 this.columnWidth = this.initialColumnWidth;
35139 // column width is fixed at the top - however if container width get's smaller we should
35142 // this bit calcs how man columns..
35144 var columnWidth = this.columnWidth += this.gutter;
35146 // calculate columns
35147 var containerWidth = this.containerWidth + this.gutter;
35149 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35150 // fix rounding errors, typically with gutters
35151 var excess = columnWidth - containerWidth % columnWidth;
35154 // if overshoot is less than a pixel, round up, otherwise floor it
35155 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35156 cols = Math[ mathMethod ]( cols );
35157 this.cols = Math.max( cols, 1 );
35158 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35160 // padding positioning..
35161 var totalColWidth = this.cols * this.columnWidth;
35162 var padavail = this.containerWidth - totalColWidth;
35163 // so for 2 columns - we need 3 'pads'
35165 var padNeeded = (1+this.cols) * this.padWidth;
35167 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35169 this.columnWidth += padExtra
35170 //this.padWidth = Math.floor(padavail / ( this.cols));
35172 // adjust colum width so that padding is fixed??
35174 // we have 3 columns ... total = width * 3
35175 // we have X left over... that should be used by
35177 //if (this.expandC) {
35185 getContainerWidth : function()
35187 /* // container is parent if fit width
35188 var container = this.isFitWidth ? this.element.parentNode : this.element;
35189 // check that this.size and size are there
35190 // IE8 triggers resize on body size change, so they might not be
35192 var size = getSize( container ); //FIXME
35193 this.containerWidth = size && size.innerWidth; //FIXME
35196 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35200 _getItemLayoutPosition : function( item ) // what is item?
35202 // we resize the item to our columnWidth..
35204 item.setWidth(this.columnWidth);
35205 item.autoBoxAdjust = false;
35207 var sz = item.getSize();
35209 // how many columns does this brick span
35210 var remainder = this.containerWidth % this.columnWidth;
35212 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35213 // round if off by 1 pixel, otherwise use ceil
35214 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35215 colSpan = Math.min( colSpan, this.cols );
35217 // normally this should be '1' as we dont' currently allow multi width columns..
35219 var colGroup = this._getColGroup( colSpan );
35220 // get the minimum Y value from the columns
35221 var minimumY = Math.min.apply( Math, colGroup );
35222 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35224 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35226 // position the brick
35228 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35229 y: this.currentSize.y + minimumY + this.padHeight
35233 // apply setHeight to necessary columns
35234 var setHeight = minimumY + sz.height + this.padHeight;
35235 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35237 var setSpan = this.cols + 1 - colGroup.length;
35238 for ( var i = 0; i < setSpan; i++ ) {
35239 this.colYs[ shortColIndex + i ] = setHeight ;
35246 * @param {Number} colSpan - number of columns the element spans
35247 * @returns {Array} colGroup
35249 _getColGroup : function( colSpan )
35251 if ( colSpan < 2 ) {
35252 // if brick spans only one column, use all the column Ys
35257 // how many different places could this brick fit horizontally
35258 var groupCount = this.cols + 1 - colSpan;
35259 // for each group potential horizontal position
35260 for ( var i = 0; i < groupCount; i++ ) {
35261 // make an array of colY values for that one group
35262 var groupColYs = this.colYs.slice( i, i + colSpan );
35263 // and get the max value of the array
35264 colGroup[i] = Math.max.apply( Math, groupColYs );
35269 _manageStamp : function( stamp )
35271 var stampSize = stamp.getSize();
35272 var offset = stamp.getBox();
35273 // get the columns that this stamp affects
35274 var firstX = this.isOriginLeft ? offset.x : offset.right;
35275 var lastX = firstX + stampSize.width;
35276 var firstCol = Math.floor( firstX / this.columnWidth );
35277 firstCol = Math.max( 0, firstCol );
35279 var lastCol = Math.floor( lastX / this.columnWidth );
35280 // lastCol should not go over if multiple of columnWidth #425
35281 lastCol -= lastX % this.columnWidth ? 0 : 1;
35282 lastCol = Math.min( this.cols - 1, lastCol );
35284 // set colYs to bottom of the stamp
35285 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35288 for ( var i = firstCol; i <= lastCol; i++ ) {
35289 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35294 _getContainerSize : function()
35296 this.maxY = Math.max.apply( Math, this.colYs );
35301 if ( this.isFitWidth ) {
35302 size.width = this._getContainerFitWidth();
35308 _getContainerFitWidth : function()
35310 var unusedCols = 0;
35311 // count unused columns
35314 if ( this.colYs[i] !== 0 ) {
35319 // fit container to columns that have been used
35320 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35323 needsResizeLayout : function()
35325 var previousWidth = this.containerWidth;
35326 this.getContainerWidth();
35327 return previousWidth !== this.containerWidth;
35342 * @class Roo.bootstrap.MasonryBrick
35343 * @extends Roo.bootstrap.Component
35344 * Bootstrap MasonryBrick class
35347 * Create a new MasonryBrick
35348 * @param {Object} config The config object
35351 Roo.bootstrap.MasonryBrick = function(config){
35353 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35355 Roo.bootstrap.MasonryBrick.register(this);
35361 * When a MasonryBrick is clcik
35362 * @param {Roo.bootstrap.MasonryBrick} this
35363 * @param {Roo.EventObject} e
35369 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35372 * @cfg {String} title
35376 * @cfg {String} html
35380 * @cfg {String} bgimage
35384 * @cfg {String} videourl
35388 * @cfg {String} cls
35392 * @cfg {String} href
35396 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35401 * @cfg {String} placetitle (center|bottom)
35406 * @cfg {Boolean} isFitContainer defalut true
35408 isFitContainer : true,
35411 * @cfg {Boolean} preventDefault defalut false
35413 preventDefault : false,
35416 * @cfg {Boolean} inverse defalut false
35418 maskInverse : false,
35420 getAutoCreate : function()
35422 if(!this.isFitContainer){
35423 return this.getSplitAutoCreate();
35426 var cls = 'masonry-brick masonry-brick-full';
35428 if(this.href.length){
35429 cls += ' masonry-brick-link';
35432 if(this.bgimage.length){
35433 cls += ' masonry-brick-image';
35436 if(this.maskInverse){
35437 cls += ' mask-inverse';
35440 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35441 cls += ' enable-mask';
35445 cls += ' masonry-' + this.size + '-brick';
35448 if(this.placetitle.length){
35450 switch (this.placetitle) {
35452 cls += ' masonry-center-title';
35455 cls += ' masonry-bottom-title';
35462 if(!this.html.length && !this.bgimage.length){
35463 cls += ' masonry-center-title';
35466 if(!this.html.length && this.bgimage.length){
35467 cls += ' masonry-bottom-title';
35472 cls += ' ' + this.cls;
35476 tag: (this.href.length) ? 'a' : 'div',
35481 cls: 'masonry-brick-mask'
35485 cls: 'masonry-brick-paragraph',
35491 if(this.href.length){
35492 cfg.href = this.href;
35495 var cn = cfg.cn[1].cn;
35497 if(this.title.length){
35500 cls: 'masonry-brick-title',
35505 if(this.html.length){
35508 cls: 'masonry-brick-text',
35513 if (!this.title.length && !this.html.length) {
35514 cfg.cn[1].cls += ' hide';
35517 if(this.bgimage.length){
35520 cls: 'masonry-brick-image-view',
35525 if(this.videourl.length){
35526 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35527 // youtube support only?
35530 cls: 'masonry-brick-image-view',
35533 allowfullscreen : true
35541 getSplitAutoCreate : function()
35543 var cls = 'masonry-brick masonry-brick-split';
35545 if(this.href.length){
35546 cls += ' masonry-brick-link';
35549 if(this.bgimage.length){
35550 cls += ' masonry-brick-image';
35554 cls += ' masonry-' + this.size + '-brick';
35557 switch (this.placetitle) {
35559 cls += ' masonry-center-title';
35562 cls += ' masonry-bottom-title';
35565 if(!this.bgimage.length){
35566 cls += ' masonry-center-title';
35569 if(this.bgimage.length){
35570 cls += ' masonry-bottom-title';
35576 cls += ' ' + this.cls;
35580 tag: (this.href.length) ? 'a' : 'div',
35585 cls: 'masonry-brick-split-head',
35589 cls: 'masonry-brick-paragraph',
35596 cls: 'masonry-brick-split-body',
35602 if(this.href.length){
35603 cfg.href = this.href;
35606 if(this.title.length){
35607 cfg.cn[0].cn[0].cn.push({
35609 cls: 'masonry-brick-title',
35614 if(this.html.length){
35615 cfg.cn[1].cn.push({
35617 cls: 'masonry-brick-text',
35622 if(this.bgimage.length){
35623 cfg.cn[0].cn.push({
35625 cls: 'masonry-brick-image-view',
35630 if(this.videourl.length){
35631 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35632 // youtube support only?
35633 cfg.cn[0].cn.cn.push({
35635 cls: 'masonry-brick-image-view',
35638 allowfullscreen : true
35645 initEvents: function()
35647 switch (this.size) {
35680 this.el.on('touchstart', this.onTouchStart, this);
35681 this.el.on('touchmove', this.onTouchMove, this);
35682 this.el.on('touchend', this.onTouchEnd, this);
35683 this.el.on('contextmenu', this.onContextMenu, this);
35685 this.el.on('mouseenter' ,this.enter, this);
35686 this.el.on('mouseleave', this.leave, this);
35687 this.el.on('click', this.onClick, this);
35690 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35691 this.parent().bricks.push(this);
35696 onClick: function(e, el)
35698 var time = this.endTimer - this.startTimer;
35699 // Roo.log(e.preventDefault());
35702 e.preventDefault();
35707 if(!this.preventDefault){
35711 e.preventDefault();
35713 if (this.activeClass != '') {
35714 this.selectBrick();
35717 this.fireEvent('click', this, e);
35720 enter: function(e, el)
35722 e.preventDefault();
35724 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35728 if(this.bgimage.length && this.html.length){
35729 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35733 leave: function(e, el)
35735 e.preventDefault();
35737 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35741 if(this.bgimage.length && this.html.length){
35742 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35746 onTouchStart: function(e, el)
35748 // e.preventDefault();
35750 this.touchmoved = false;
35752 if(!this.isFitContainer){
35756 if(!this.bgimage.length || !this.html.length){
35760 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35762 this.timer = new Date().getTime();
35766 onTouchMove: function(e, el)
35768 this.touchmoved = true;
35771 onContextMenu : function(e,el)
35773 e.preventDefault();
35774 e.stopPropagation();
35778 onTouchEnd: function(e, el)
35780 // e.preventDefault();
35782 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35789 if(!this.bgimage.length || !this.html.length){
35791 if(this.href.length){
35792 window.location.href = this.href;
35798 if(!this.isFitContainer){
35802 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35804 window.location.href = this.href;
35807 //selection on single brick only
35808 selectBrick : function() {
35810 if (!this.parentId) {
35814 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35815 var index = m.selectedBrick.indexOf(this.id);
35818 m.selectedBrick.splice(index,1);
35819 this.el.removeClass(this.activeClass);
35823 for(var i = 0; i < m.selectedBrick.length; i++) {
35824 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35825 b.el.removeClass(b.activeClass);
35828 m.selectedBrick = [];
35830 m.selectedBrick.push(this.id);
35831 this.el.addClass(this.activeClass);
35835 isSelected : function(){
35836 return this.el.hasClass(this.activeClass);
35841 Roo.apply(Roo.bootstrap.MasonryBrick, {
35844 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35846 * register a Masonry Brick
35847 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35850 register : function(brick)
35852 //this.groups[brick.id] = brick;
35853 this.groups.add(brick.id, brick);
35856 * fetch a masonry brick based on the masonry brick ID
35857 * @param {string} the masonry brick to add
35858 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35861 get: function(brick_id)
35863 // if (typeof(this.groups[brick_id]) == 'undefined') {
35866 // return this.groups[brick_id] ;
35868 if(this.groups.key(brick_id)) {
35869 return this.groups.key(brick_id);
35887 * @class Roo.bootstrap.Brick
35888 * @extends Roo.bootstrap.Component
35889 * Bootstrap Brick class
35892 * Create a new Brick
35893 * @param {Object} config The config object
35896 Roo.bootstrap.Brick = function(config){
35897 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35903 * When a Brick is click
35904 * @param {Roo.bootstrap.Brick} this
35905 * @param {Roo.EventObject} e
35911 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35914 * @cfg {String} title
35918 * @cfg {String} html
35922 * @cfg {String} bgimage
35926 * @cfg {String} cls
35930 * @cfg {String} href
35934 * @cfg {String} video
35938 * @cfg {Boolean} square
35942 getAutoCreate : function()
35944 var cls = 'roo-brick';
35946 if(this.href.length){
35947 cls += ' roo-brick-link';
35950 if(this.bgimage.length){
35951 cls += ' roo-brick-image';
35954 if(!this.html.length && !this.bgimage.length){
35955 cls += ' roo-brick-center-title';
35958 if(!this.html.length && this.bgimage.length){
35959 cls += ' roo-brick-bottom-title';
35963 cls += ' ' + this.cls;
35967 tag: (this.href.length) ? 'a' : 'div',
35972 cls: 'roo-brick-paragraph',
35978 if(this.href.length){
35979 cfg.href = this.href;
35982 var cn = cfg.cn[0].cn;
35984 if(this.title.length){
35987 cls: 'roo-brick-title',
35992 if(this.html.length){
35995 cls: 'roo-brick-text',
36002 if(this.bgimage.length){
36005 cls: 'roo-brick-image-view',
36013 initEvents: function()
36015 if(this.title.length || this.html.length){
36016 this.el.on('mouseenter' ,this.enter, this);
36017 this.el.on('mouseleave', this.leave, this);
36020 Roo.EventManager.onWindowResize(this.resize, this);
36022 if(this.bgimage.length){
36023 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36024 this.imageEl.on('load', this.onImageLoad, this);
36031 onImageLoad : function()
36036 resize : function()
36038 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36040 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36042 if(this.bgimage.length){
36043 var image = this.el.select('.roo-brick-image-view', true).first();
36045 image.setWidth(paragraph.getWidth());
36048 image.setHeight(paragraph.getWidth());
36051 this.el.setHeight(image.getHeight());
36052 paragraph.setHeight(image.getHeight());
36058 enter: function(e, el)
36060 e.preventDefault();
36062 if(this.bgimage.length){
36063 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36064 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36068 leave: function(e, el)
36070 e.preventDefault();
36072 if(this.bgimage.length){
36073 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36074 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36089 * @class Roo.bootstrap.NumberField
36090 * @extends Roo.bootstrap.Input
36091 * Bootstrap NumberField class
36097 * Create a new NumberField
36098 * @param {Object} config The config object
36101 Roo.bootstrap.NumberField = function(config){
36102 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36105 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36108 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36110 allowDecimals : true,
36112 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36114 decimalSeparator : ".",
36116 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36118 decimalPrecision : 2,
36120 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36122 allowNegative : true,
36125 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36129 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36131 minValue : Number.NEGATIVE_INFINITY,
36133 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36135 maxValue : Number.MAX_VALUE,
36137 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36139 minText : "The minimum value for this field is {0}",
36141 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36143 maxText : "The maximum value for this field is {0}",
36145 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36146 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36148 nanText : "{0} is not a valid number",
36150 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36152 thousandsDelimiter : false,
36154 * @cfg {String} valueAlign alignment of value
36156 valueAlign : "left",
36158 getAutoCreate : function()
36160 var hiddenInput = {
36164 cls: 'hidden-number-input'
36168 hiddenInput.name = this.name;
36173 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36175 this.name = hiddenInput.name;
36177 if(cfg.cn.length > 0) {
36178 cfg.cn.push(hiddenInput);
36185 initEvents : function()
36187 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36189 var allowed = "0123456789";
36191 if(this.allowDecimals){
36192 allowed += this.decimalSeparator;
36195 if(this.allowNegative){
36199 if(this.thousandsDelimiter) {
36203 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36205 var keyPress = function(e){
36207 var k = e.getKey();
36209 var c = e.getCharCode();
36212 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36213 allowed.indexOf(String.fromCharCode(c)) === -1
36219 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36223 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36228 this.el.on("keypress", keyPress, this);
36231 validateValue : function(value)
36234 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36238 var num = this.parseValue(value);
36241 this.markInvalid(String.format(this.nanText, value));
36245 if(num < this.minValue){
36246 this.markInvalid(String.format(this.minText, this.minValue));
36250 if(num > this.maxValue){
36251 this.markInvalid(String.format(this.maxText, this.maxValue));
36258 getValue : function()
36260 var v = this.hiddenEl().getValue();
36262 return this.fixPrecision(this.parseValue(v));
36265 parseValue : function(value)
36267 if(this.thousandsDelimiter) {
36269 r = new RegExp(",", "g");
36270 value = value.replace(r, "");
36273 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36274 return isNaN(value) ? '' : value;
36277 fixPrecision : function(value)
36279 if(this.thousandsDelimiter) {
36281 r = new RegExp(",", "g");
36282 value = value.replace(r, "");
36285 var nan = isNaN(value);
36287 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36288 return nan ? '' : value;
36290 return parseFloat(value).toFixed(this.decimalPrecision);
36293 setValue : function(v)
36295 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36301 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36303 this.inputEl().dom.value = (v == '') ? '' :
36304 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36306 if(!this.allowZero && v === '0') {
36307 this.hiddenEl().dom.value = '';
36308 this.inputEl().dom.value = '';
36315 decimalPrecisionFcn : function(v)
36317 return Math.floor(v);
36320 beforeBlur : function()
36322 var v = this.parseValue(this.getRawValue());
36324 if(v || v === 0 || v === ''){
36329 hiddenEl : function()
36331 return this.el.select('input.hidden-number-input',true).first();
36343 * @class Roo.bootstrap.DocumentSlider
36344 * @extends Roo.bootstrap.Component
36345 * Bootstrap DocumentSlider class
36348 * Create a new DocumentViewer
36349 * @param {Object} config The config object
36352 Roo.bootstrap.DocumentSlider = function(config){
36353 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36360 * Fire after initEvent
36361 * @param {Roo.bootstrap.DocumentSlider} this
36366 * Fire after update
36367 * @param {Roo.bootstrap.DocumentSlider} this
36373 * @param {Roo.bootstrap.DocumentSlider} this
36379 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36385 getAutoCreate : function()
36389 cls : 'roo-document-slider',
36393 cls : 'roo-document-slider-header',
36397 cls : 'roo-document-slider-header-title'
36403 cls : 'roo-document-slider-body',
36407 cls : 'roo-document-slider-prev',
36411 cls : 'fa fa-chevron-left'
36417 cls : 'roo-document-slider-thumb',
36421 cls : 'roo-document-slider-image'
36427 cls : 'roo-document-slider-next',
36431 cls : 'fa fa-chevron-right'
36443 initEvents : function()
36445 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36446 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36448 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36449 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36451 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36452 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36454 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36455 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36457 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36458 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36460 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36461 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36463 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36464 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36466 this.thumbEl.on('click', this.onClick, this);
36468 this.prevIndicator.on('click', this.prev, this);
36470 this.nextIndicator.on('click', this.next, this);
36474 initial : function()
36476 if(this.files.length){
36477 this.indicator = 1;
36481 this.fireEvent('initial', this);
36484 update : function()
36486 this.imageEl.attr('src', this.files[this.indicator - 1]);
36488 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36490 this.prevIndicator.show();
36492 if(this.indicator == 1){
36493 this.prevIndicator.hide();
36496 this.nextIndicator.show();
36498 if(this.indicator == this.files.length){
36499 this.nextIndicator.hide();
36502 this.thumbEl.scrollTo('top');
36504 this.fireEvent('update', this);
36507 onClick : function(e)
36509 e.preventDefault();
36511 this.fireEvent('click', this);
36516 e.preventDefault();
36518 this.indicator = Math.max(1, this.indicator - 1);
36525 e.preventDefault();
36527 this.indicator = Math.min(this.files.length, this.indicator + 1);
36541 * @class Roo.bootstrap.RadioSet
36542 * @extends Roo.bootstrap.Input
36543 * Bootstrap RadioSet class
36544 * @cfg {String} indicatorpos (left|right) default left
36545 * @cfg {Boolean} inline (true|false) inline the element (default true)
36546 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36548 * Create a new RadioSet
36549 * @param {Object} config The config object
36552 Roo.bootstrap.RadioSet = function(config){
36554 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36558 Roo.bootstrap.RadioSet.register(this);
36563 * Fires when the element is checked or unchecked.
36564 * @param {Roo.bootstrap.RadioSet} this This radio
36565 * @param {Roo.bootstrap.Radio} item The checked item
36570 * Fires when the element is click.
36571 * @param {Roo.bootstrap.RadioSet} this This radio set
36572 * @param {Roo.bootstrap.Radio} item The checked item
36573 * @param {Roo.EventObject} e The event object
36580 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36588 indicatorpos : 'left',
36590 getAutoCreate : function()
36594 cls : 'roo-radio-set-label',
36598 html : this.fieldLabel
36602 if (Roo.bootstrap.version == 3) {
36605 if(this.indicatorpos == 'left'){
36608 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36609 tooltip : 'This field is required'
36614 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36615 tooltip : 'This field is required'
36621 cls : 'roo-radio-set-items'
36624 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36626 if (align === 'left' && this.fieldLabel.length) {
36629 cls : "roo-radio-set-right",
36635 if(this.labelWidth > 12){
36636 label.style = "width: " + this.labelWidth + 'px';
36639 if(this.labelWidth < 13 && this.labelmd == 0){
36640 this.labelmd = this.labelWidth;
36643 if(this.labellg > 0){
36644 label.cls += ' col-lg-' + this.labellg;
36645 items.cls += ' col-lg-' + (12 - this.labellg);
36648 if(this.labelmd > 0){
36649 label.cls += ' col-md-' + this.labelmd;
36650 items.cls += ' col-md-' + (12 - this.labelmd);
36653 if(this.labelsm > 0){
36654 label.cls += ' col-sm-' + this.labelsm;
36655 items.cls += ' col-sm-' + (12 - this.labelsm);
36658 if(this.labelxs > 0){
36659 label.cls += ' col-xs-' + this.labelxs;
36660 items.cls += ' col-xs-' + (12 - this.labelxs);
36666 cls : 'roo-radio-set',
36670 cls : 'roo-radio-set-input',
36673 value : this.value ? this.value : ''
36680 if(this.weight.length){
36681 cfg.cls += ' roo-radio-' + this.weight;
36685 cfg.cls += ' roo-radio-set-inline';
36689 ['xs','sm','md','lg'].map(function(size){
36690 if (settings[size]) {
36691 cfg.cls += ' col-' + size + '-' + settings[size];
36699 initEvents : function()
36701 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36702 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36704 if(!this.fieldLabel.length){
36705 this.labelEl.hide();
36708 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36709 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36711 this.indicator = this.indicatorEl();
36713 if(this.indicator){
36714 this.indicator.addClass('invisible');
36717 this.originalValue = this.getValue();
36721 inputEl: function ()
36723 return this.el.select('.roo-radio-set-input', true).first();
36726 getChildContainer : function()
36728 return this.itemsEl;
36731 register : function(item)
36733 this.radioes.push(item);
36737 validate : function()
36739 if(this.getVisibilityEl().hasClass('hidden')){
36745 Roo.each(this.radioes, function(i){
36754 if(this.allowBlank) {
36758 if(this.disabled || valid){
36763 this.markInvalid();
36768 markValid : function()
36770 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36771 this.indicatorEl().removeClass('visible');
36772 this.indicatorEl().addClass('invisible');
36776 if (Roo.bootstrap.version == 3) {
36777 this.el.removeClass([this.invalidClass, this.validClass]);
36778 this.el.addClass(this.validClass);
36780 this.el.removeClass(['is-invalid','is-valid']);
36781 this.el.addClass(['is-valid']);
36783 this.fireEvent('valid', this);
36786 markInvalid : function(msg)
36788 if(this.allowBlank || this.disabled){
36792 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36793 this.indicatorEl().removeClass('invisible');
36794 this.indicatorEl().addClass('visible');
36796 if (Roo.bootstrap.version == 3) {
36797 this.el.removeClass([this.invalidClass, this.validClass]);
36798 this.el.addClass(this.invalidClass);
36800 this.el.removeClass(['is-invalid','is-valid']);
36801 this.el.addClass(['is-invalid']);
36804 this.fireEvent('invalid', this, msg);
36808 setValue : function(v, suppressEvent)
36810 if(this.value === v){
36817 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36820 Roo.each(this.radioes, function(i){
36822 i.el.removeClass('checked');
36825 Roo.each(this.radioes, function(i){
36827 if(i.value === v || i.value.toString() === v.toString()){
36829 i.el.addClass('checked');
36831 if(suppressEvent !== true){
36832 this.fireEvent('check', this, i);
36843 clearInvalid : function(){
36845 if(!this.el || this.preventMark){
36849 this.el.removeClass([this.invalidClass]);
36851 this.fireEvent('valid', this);
36856 Roo.apply(Roo.bootstrap.RadioSet, {
36860 register : function(set)
36862 this.groups[set.name] = set;
36865 get: function(name)
36867 if (typeof(this.groups[name]) == 'undefined') {
36871 return this.groups[name] ;
36877 * Ext JS Library 1.1.1
36878 * Copyright(c) 2006-2007, Ext JS, LLC.
36880 * Originally Released Under LGPL - original licence link has changed is not relivant.
36883 * <script type="text/javascript">
36888 * @class Roo.bootstrap.SplitBar
36889 * @extends Roo.util.Observable
36890 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36894 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36895 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36896 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36897 split.minSize = 100;
36898 split.maxSize = 600;
36899 split.animate = true;
36900 split.on('moved', splitterMoved);
36903 * Create a new SplitBar
36904 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36905 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36906 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36907 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36908 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36909 position of the SplitBar).
36911 Roo.bootstrap.SplitBar = function(cfg){
36916 // dragElement : elm
36917 // resizingElement: el,
36919 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36920 // placement : Roo.bootstrap.SplitBar.LEFT ,
36921 // existingProxy ???
36924 this.el = Roo.get(cfg.dragElement, true);
36925 this.el.dom.unselectable = "on";
36927 this.resizingEl = Roo.get(cfg.resizingElement, true);
36931 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36932 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36935 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36938 * The minimum size of the resizing element. (Defaults to 0)
36944 * The maximum size of the resizing element. (Defaults to 2000)
36947 this.maxSize = 2000;
36950 * Whether to animate the transition to the new size
36953 this.animate = false;
36956 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36959 this.useShim = false;
36964 if(!cfg.existingProxy){
36966 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36968 this.proxy = Roo.get(cfg.existingProxy).dom;
36971 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36974 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36977 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36980 this.dragSpecs = {};
36983 * @private The adapter to use to positon and resize elements
36985 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36986 this.adapter.init(this);
36988 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36990 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36991 this.el.addClass("roo-splitbar-h");
36994 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36995 this.el.addClass("roo-splitbar-v");
37001 * Fires when the splitter is moved (alias for {@link #event-moved})
37002 * @param {Roo.bootstrap.SplitBar} this
37003 * @param {Number} newSize the new width or height
37008 * Fires when the splitter is moved
37009 * @param {Roo.bootstrap.SplitBar} this
37010 * @param {Number} newSize the new width or height
37014 * @event beforeresize
37015 * Fires before the splitter is dragged
37016 * @param {Roo.bootstrap.SplitBar} this
37018 "beforeresize" : true,
37020 "beforeapply" : true
37023 Roo.util.Observable.call(this);
37026 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37027 onStartProxyDrag : function(x, y){
37028 this.fireEvent("beforeresize", this);
37030 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37032 o.enableDisplayMode("block");
37033 // all splitbars share the same overlay
37034 Roo.bootstrap.SplitBar.prototype.overlay = o;
37036 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37037 this.overlay.show();
37038 Roo.get(this.proxy).setDisplayed("block");
37039 var size = this.adapter.getElementSize(this);
37040 this.activeMinSize = this.getMinimumSize();;
37041 this.activeMaxSize = this.getMaximumSize();;
37042 var c1 = size - this.activeMinSize;
37043 var c2 = Math.max(this.activeMaxSize - size, 0);
37044 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37045 this.dd.resetConstraints();
37046 this.dd.setXConstraint(
37047 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37048 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37050 this.dd.setYConstraint(0, 0);
37052 this.dd.resetConstraints();
37053 this.dd.setXConstraint(0, 0);
37054 this.dd.setYConstraint(
37055 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37056 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37059 this.dragSpecs.startSize = size;
37060 this.dragSpecs.startPoint = [x, y];
37061 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37065 * @private Called after the drag operation by the DDProxy
37067 onEndProxyDrag : function(e){
37068 Roo.get(this.proxy).setDisplayed(false);
37069 var endPoint = Roo.lib.Event.getXY(e);
37071 this.overlay.hide();
37074 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37075 newSize = this.dragSpecs.startSize +
37076 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37077 endPoint[0] - this.dragSpecs.startPoint[0] :
37078 this.dragSpecs.startPoint[0] - endPoint[0]
37081 newSize = this.dragSpecs.startSize +
37082 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37083 endPoint[1] - this.dragSpecs.startPoint[1] :
37084 this.dragSpecs.startPoint[1] - endPoint[1]
37087 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37088 if(newSize != this.dragSpecs.startSize){
37089 if(this.fireEvent('beforeapply', this, newSize) !== false){
37090 this.adapter.setElementSize(this, newSize);
37091 this.fireEvent("moved", this, newSize);
37092 this.fireEvent("resize", this, newSize);
37098 * Get the adapter this SplitBar uses
37099 * @return The adapter object
37101 getAdapter : function(){
37102 return this.adapter;
37106 * Set the adapter this SplitBar uses
37107 * @param {Object} adapter A SplitBar adapter object
37109 setAdapter : function(adapter){
37110 this.adapter = adapter;
37111 this.adapter.init(this);
37115 * Gets the minimum size for the resizing element
37116 * @return {Number} The minimum size
37118 getMinimumSize : function(){
37119 return this.minSize;
37123 * Sets the minimum size for the resizing element
37124 * @param {Number} minSize The minimum size
37126 setMinimumSize : function(minSize){
37127 this.minSize = minSize;
37131 * Gets the maximum size for the resizing element
37132 * @return {Number} The maximum size
37134 getMaximumSize : function(){
37135 return this.maxSize;
37139 * Sets the maximum size for the resizing element
37140 * @param {Number} maxSize The maximum size
37142 setMaximumSize : function(maxSize){
37143 this.maxSize = maxSize;
37147 * Sets the initialize size for the resizing element
37148 * @param {Number} size The initial size
37150 setCurrentSize : function(size){
37151 var oldAnimate = this.animate;
37152 this.animate = false;
37153 this.adapter.setElementSize(this, size);
37154 this.animate = oldAnimate;
37158 * Destroy this splitbar.
37159 * @param {Boolean} removeEl True to remove the element
37161 destroy : function(removeEl){
37163 this.shim.remove();
37166 this.proxy.parentNode.removeChild(this.proxy);
37174 * @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.
37176 Roo.bootstrap.SplitBar.createProxy = function(dir){
37177 var proxy = new Roo.Element(document.createElement("div"));
37178 proxy.unselectable();
37179 var cls = 'roo-splitbar-proxy';
37180 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37181 document.body.appendChild(proxy.dom);
37186 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37187 * Default Adapter. It assumes the splitter and resizing element are not positioned
37188 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37190 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37193 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37194 // do nothing for now
37195 init : function(s){
37199 * Called before drag operations to get the current size of the resizing element.
37200 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37202 getElementSize : function(s){
37203 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37204 return s.resizingEl.getWidth();
37206 return s.resizingEl.getHeight();
37211 * Called after drag operations to set the size of the resizing element.
37212 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37213 * @param {Number} newSize The new size to set
37214 * @param {Function} onComplete A function to be invoked when resizing is complete
37216 setElementSize : function(s, newSize, onComplete){
37217 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37219 s.resizingEl.setWidth(newSize);
37221 onComplete(s, newSize);
37224 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37229 s.resizingEl.setHeight(newSize);
37231 onComplete(s, newSize);
37234 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37241 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37242 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37243 * Adapter that moves the splitter element to align with the resized sizing element.
37244 * Used with an absolute positioned SplitBar.
37245 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37246 * document.body, make sure you assign an id to the body element.
37248 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37249 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37250 this.container = Roo.get(container);
37253 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37254 init : function(s){
37255 this.basic.init(s);
37258 getElementSize : function(s){
37259 return this.basic.getElementSize(s);
37262 setElementSize : function(s, newSize, onComplete){
37263 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37266 moveSplitter : function(s){
37267 var yes = Roo.bootstrap.SplitBar;
37268 switch(s.placement){
37270 s.el.setX(s.resizingEl.getRight());
37273 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37276 s.el.setY(s.resizingEl.getBottom());
37279 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37286 * Orientation constant - Create a vertical SplitBar
37290 Roo.bootstrap.SplitBar.VERTICAL = 1;
37293 * Orientation constant - Create a horizontal SplitBar
37297 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37300 * Placement constant - The resizing element is to the left of the splitter element
37304 Roo.bootstrap.SplitBar.LEFT = 1;
37307 * Placement constant - The resizing element is to the right of the splitter element
37311 Roo.bootstrap.SplitBar.RIGHT = 2;
37314 * Placement constant - The resizing element is positioned above the splitter element
37318 Roo.bootstrap.SplitBar.TOP = 3;
37321 * Placement constant - The resizing element is positioned under splitter element
37325 Roo.bootstrap.SplitBar.BOTTOM = 4;
37326 Roo.namespace("Roo.bootstrap.layout");/*
37328 * Ext JS Library 1.1.1
37329 * Copyright(c) 2006-2007, Ext JS, LLC.
37331 * Originally Released Under LGPL - original licence link has changed is not relivant.
37334 * <script type="text/javascript">
37338 * @class Roo.bootstrap.layout.Manager
37339 * @extends Roo.bootstrap.Component
37340 * Base class for layout managers.
37342 Roo.bootstrap.layout.Manager = function(config)
37344 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37350 /** false to disable window resize monitoring @type Boolean */
37351 this.monitorWindowResize = true;
37356 * Fires when a layout is performed.
37357 * @param {Roo.LayoutManager} this
37361 * @event regionresized
37362 * Fires when the user resizes a region.
37363 * @param {Roo.LayoutRegion} region The resized region
37364 * @param {Number} newSize The new size (width for east/west, height for north/south)
37366 "regionresized" : true,
37368 * @event regioncollapsed
37369 * Fires when a region is collapsed.
37370 * @param {Roo.LayoutRegion} region The collapsed region
37372 "regioncollapsed" : true,
37374 * @event regionexpanded
37375 * Fires when a region is expanded.
37376 * @param {Roo.LayoutRegion} region The expanded region
37378 "regionexpanded" : true
37380 this.updating = false;
37383 this.el = Roo.get(config.el);
37389 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37394 monitorWindowResize : true,
37400 onRender : function(ct, position)
37403 this.el = Roo.get(ct);
37406 //this.fireEvent('render',this);
37410 initEvents: function()
37414 // ie scrollbar fix
37415 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37416 document.body.scroll = "no";
37417 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37418 this.el.position('relative');
37420 this.id = this.el.id;
37421 this.el.addClass("roo-layout-container");
37422 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37423 if(this.el.dom != document.body ) {
37424 this.el.on('resize', this.layout,this);
37425 this.el.on('show', this.layout,this);
37431 * Returns true if this layout is currently being updated
37432 * @return {Boolean}
37434 isUpdating : function(){
37435 return this.updating;
37439 * Suspend the LayoutManager from doing auto-layouts while
37440 * making multiple add or remove calls
37442 beginUpdate : function(){
37443 this.updating = true;
37447 * Restore auto-layouts and optionally disable the manager from performing a layout
37448 * @param {Boolean} noLayout true to disable a layout update
37450 endUpdate : function(noLayout){
37451 this.updating = false;
37457 layout: function(){
37461 onRegionResized : function(region, newSize){
37462 this.fireEvent("regionresized", region, newSize);
37466 onRegionCollapsed : function(region){
37467 this.fireEvent("regioncollapsed", region);
37470 onRegionExpanded : function(region){
37471 this.fireEvent("regionexpanded", region);
37475 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37476 * performs box-model adjustments.
37477 * @return {Object} The size as an object {width: (the width), height: (the height)}
37479 getViewSize : function()
37482 if(this.el.dom != document.body){
37483 size = this.el.getSize();
37485 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37487 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37488 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37493 * Returns the Element this layout is bound to.
37494 * @return {Roo.Element}
37496 getEl : function(){
37501 * Returns the specified region.
37502 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37503 * @return {Roo.LayoutRegion}
37505 getRegion : function(target){
37506 return this.regions[target.toLowerCase()];
37509 onWindowResize : function(){
37510 if(this.monitorWindowResize){
37517 * Ext JS Library 1.1.1
37518 * Copyright(c) 2006-2007, Ext JS, LLC.
37520 * Originally Released Under LGPL - original licence link has changed is not relivant.
37523 * <script type="text/javascript">
37526 * @class Roo.bootstrap.layout.Border
37527 * @extends Roo.bootstrap.layout.Manager
37528 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37529 * please see: examples/bootstrap/nested.html<br><br>
37531 <b>The container the layout is rendered into can be either the body element or any other element.
37532 If it is not the body element, the container needs to either be an absolute positioned element,
37533 or you will need to add "position:relative" to the css of the container. You will also need to specify
37534 the container size if it is not the body element.</b>
37537 * Create a new Border
37538 * @param {Object} config Configuration options
37540 Roo.bootstrap.layout.Border = function(config){
37541 config = config || {};
37542 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37546 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37547 if(config[region]){
37548 config[region].region = region;
37549 this.addRegion(config[region]);
37555 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37557 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37559 parent : false, // this might point to a 'nest' or a ???
37562 * Creates and adds a new region if it doesn't already exist.
37563 * @param {String} target The target region key (north, south, east, west or center).
37564 * @param {Object} config The regions config object
37565 * @return {BorderLayoutRegion} The new region
37567 addRegion : function(config)
37569 if(!this.regions[config.region]){
37570 var r = this.factory(config);
37571 this.bindRegion(r);
37573 return this.regions[config.region];
37577 bindRegion : function(r){
37578 this.regions[r.config.region] = r;
37580 r.on("visibilitychange", this.layout, this);
37581 r.on("paneladded", this.layout, this);
37582 r.on("panelremoved", this.layout, this);
37583 r.on("invalidated", this.layout, this);
37584 r.on("resized", this.onRegionResized, this);
37585 r.on("collapsed", this.onRegionCollapsed, this);
37586 r.on("expanded", this.onRegionExpanded, this);
37590 * Performs a layout update.
37592 layout : function()
37594 if(this.updating) {
37598 // render all the rebions if they have not been done alreayd?
37599 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37600 if(this.regions[region] && !this.regions[region].bodyEl){
37601 this.regions[region].onRender(this.el)
37605 var size = this.getViewSize();
37606 var w = size.width;
37607 var h = size.height;
37612 //var x = 0, y = 0;
37614 var rs = this.regions;
37615 var north = rs["north"];
37616 var south = rs["south"];
37617 var west = rs["west"];
37618 var east = rs["east"];
37619 var center = rs["center"];
37620 //if(this.hideOnLayout){ // not supported anymore
37621 //c.el.setStyle("display", "none");
37623 if(north && north.isVisible()){
37624 var b = north.getBox();
37625 var m = north.getMargins();
37626 b.width = w - (m.left+m.right);
37629 centerY = b.height + b.y + m.bottom;
37630 centerH -= centerY;
37631 north.updateBox(this.safeBox(b));
37633 if(south && south.isVisible()){
37634 var b = south.getBox();
37635 var m = south.getMargins();
37636 b.width = w - (m.left+m.right);
37638 var totalHeight = (b.height + m.top + m.bottom);
37639 b.y = h - totalHeight + m.top;
37640 centerH -= totalHeight;
37641 south.updateBox(this.safeBox(b));
37643 if(west && west.isVisible()){
37644 var b = west.getBox();
37645 var m = west.getMargins();
37646 b.height = centerH - (m.top+m.bottom);
37648 b.y = centerY + m.top;
37649 var totalWidth = (b.width + m.left + m.right);
37650 centerX += totalWidth;
37651 centerW -= totalWidth;
37652 west.updateBox(this.safeBox(b));
37654 if(east && east.isVisible()){
37655 var b = east.getBox();
37656 var m = east.getMargins();
37657 b.height = centerH - (m.top+m.bottom);
37658 var totalWidth = (b.width + m.left + m.right);
37659 b.x = w - totalWidth + m.left;
37660 b.y = centerY + m.top;
37661 centerW -= totalWidth;
37662 east.updateBox(this.safeBox(b));
37665 var m = center.getMargins();
37667 x: centerX + m.left,
37668 y: centerY + m.top,
37669 width: centerW - (m.left+m.right),
37670 height: centerH - (m.top+m.bottom)
37672 //if(this.hideOnLayout){
37673 //center.el.setStyle("display", "block");
37675 center.updateBox(this.safeBox(centerBox));
37678 this.fireEvent("layout", this);
37682 safeBox : function(box){
37683 box.width = Math.max(0, box.width);
37684 box.height = Math.max(0, box.height);
37689 * Adds a ContentPanel (or subclass) to this layout.
37690 * @param {String} target The target region key (north, south, east, west or center).
37691 * @param {Roo.ContentPanel} panel The panel to add
37692 * @return {Roo.ContentPanel} The added panel
37694 add : function(target, panel){
37696 target = target.toLowerCase();
37697 return this.regions[target].add(panel);
37701 * Remove a ContentPanel (or subclass) to this layout.
37702 * @param {String} target The target region key (north, south, east, west or center).
37703 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37704 * @return {Roo.ContentPanel} The removed panel
37706 remove : function(target, panel){
37707 target = target.toLowerCase();
37708 return this.regions[target].remove(panel);
37712 * Searches all regions for a panel with the specified id
37713 * @param {String} panelId
37714 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37716 findPanel : function(panelId){
37717 var rs = this.regions;
37718 for(var target in rs){
37719 if(typeof rs[target] != "function"){
37720 var p = rs[target].getPanel(panelId);
37730 * Searches all regions for a panel with the specified id and activates (shows) it.
37731 * @param {String/ContentPanel} panelId The panels id or the panel itself
37732 * @return {Roo.ContentPanel} The shown panel or null
37734 showPanel : function(panelId) {
37735 var rs = this.regions;
37736 for(var target in rs){
37737 var r = rs[target];
37738 if(typeof r != "function"){
37739 if(r.hasPanel(panelId)){
37740 return r.showPanel(panelId);
37748 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37749 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37752 restoreState : function(provider){
37754 provider = Roo.state.Manager;
37756 var sm = new Roo.LayoutStateManager();
37757 sm.init(this, provider);
37763 * Adds a xtype elements to the layout.
37767 xtype : 'ContentPanel',
37774 xtype : 'NestedLayoutPanel',
37780 items : [ ... list of content panels or nested layout panels.. ]
37784 * @param {Object} cfg Xtype definition of item to add.
37786 addxtype : function(cfg)
37788 // basically accepts a pannel...
37789 // can accept a layout region..!?!?
37790 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37793 // theory? children can only be panels??
37795 //if (!cfg.xtype.match(/Panel$/)) {
37800 if (typeof(cfg.region) == 'undefined') {
37801 Roo.log("Failed to add Panel, region was not set");
37805 var region = cfg.region;
37811 xitems = cfg.items;
37816 if ( region == 'center') {
37817 Roo.log("Center: " + cfg.title);
37823 case 'Content': // ContentPanel (el, cfg)
37824 case 'Scroll': // ContentPanel (el, cfg)
37826 cfg.autoCreate = cfg.autoCreate || true;
37827 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37829 // var el = this.el.createChild();
37830 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37833 this.add(region, ret);
37837 case 'TreePanel': // our new panel!
37838 cfg.el = this.el.createChild();
37839 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37840 this.add(region, ret);
37845 // create a new Layout (which is a Border Layout...
37847 var clayout = cfg.layout;
37848 clayout.el = this.el.createChild();
37849 clayout.items = clayout.items || [];
37853 // replace this exitems with the clayout ones..
37854 xitems = clayout.items;
37856 // force background off if it's in center...
37857 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37858 cfg.background = false;
37860 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37863 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37864 //console.log('adding nested layout panel ' + cfg.toSource());
37865 this.add(region, ret);
37866 nb = {}; /// find first...
37871 // needs grid and region
37873 //var el = this.getRegion(region).el.createChild();
37875 *var el = this.el.createChild();
37876 // create the grid first...
37877 cfg.grid.container = el;
37878 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37881 if (region == 'center' && this.active ) {
37882 cfg.background = false;
37885 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37887 this.add(region, ret);
37889 if (cfg.background) {
37890 // render grid on panel activation (if panel background)
37891 ret.on('activate', function(gp) {
37892 if (!gp.grid.rendered) {
37893 // gp.grid.render(el);
37897 // cfg.grid.render(el);
37903 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37904 // it was the old xcomponent building that caused this before.
37905 // espeically if border is the top element in the tree.
37915 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37917 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37918 this.add(region, ret);
37922 throw "Can not add '" + cfg.xtype + "' to Border";
37928 this.beginUpdate();
37932 Roo.each(xitems, function(i) {
37933 region = nb && i.region ? i.region : false;
37935 var add = ret.addxtype(i);
37938 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37939 if (!i.background) {
37940 abn[region] = nb[region] ;
37947 // make the last non-background panel active..
37948 //if (nb) { Roo.log(abn); }
37951 for(var r in abn) {
37952 region = this.getRegion(r);
37954 // tried using nb[r], but it does not work..
37956 region.showPanel(abn[r]);
37967 factory : function(cfg)
37970 var validRegions = Roo.bootstrap.layout.Border.regions;
37972 var target = cfg.region;
37975 var r = Roo.bootstrap.layout;
37979 return new r.North(cfg);
37981 return new r.South(cfg);
37983 return new r.East(cfg);
37985 return new r.West(cfg);
37987 return new r.Center(cfg);
37989 throw 'Layout region "'+target+'" not supported.';
37996 * Ext JS Library 1.1.1
37997 * Copyright(c) 2006-2007, Ext JS, LLC.
37999 * Originally Released Under LGPL - original licence link has changed is not relivant.
38002 * <script type="text/javascript">
38006 * @class Roo.bootstrap.layout.Basic
38007 * @extends Roo.util.Observable
38008 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38009 * and does not have a titlebar, tabs or any other features. All it does is size and position
38010 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38011 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38012 * @cfg {string} region the region that it inhabits..
38013 * @cfg {bool} skipConfig skip config?
38017 Roo.bootstrap.layout.Basic = function(config){
38019 this.mgr = config.mgr;
38021 this.position = config.region;
38023 var skipConfig = config.skipConfig;
38027 * @scope Roo.BasicLayoutRegion
38031 * @event beforeremove
38032 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38033 * @param {Roo.LayoutRegion} this
38034 * @param {Roo.ContentPanel} panel The panel
38035 * @param {Object} e The cancel event object
38037 "beforeremove" : true,
38039 * @event invalidated
38040 * Fires when the layout for this region is changed.
38041 * @param {Roo.LayoutRegion} this
38043 "invalidated" : true,
38045 * @event visibilitychange
38046 * Fires when this region is shown or hidden
38047 * @param {Roo.LayoutRegion} this
38048 * @param {Boolean} visibility true or false
38050 "visibilitychange" : true,
38052 * @event paneladded
38053 * Fires when a panel is added.
38054 * @param {Roo.LayoutRegion} this
38055 * @param {Roo.ContentPanel} panel The panel
38057 "paneladded" : true,
38059 * @event panelremoved
38060 * Fires when a panel is removed.
38061 * @param {Roo.LayoutRegion} this
38062 * @param {Roo.ContentPanel} panel The panel
38064 "panelremoved" : true,
38066 * @event beforecollapse
38067 * Fires when this region before collapse.
38068 * @param {Roo.LayoutRegion} this
38070 "beforecollapse" : true,
38073 * Fires when this region is collapsed.
38074 * @param {Roo.LayoutRegion} this
38076 "collapsed" : true,
38079 * Fires when this region is expanded.
38080 * @param {Roo.LayoutRegion} this
38085 * Fires when this region is slid into view.
38086 * @param {Roo.LayoutRegion} this
38088 "slideshow" : true,
38091 * Fires when this region slides out of view.
38092 * @param {Roo.LayoutRegion} this
38094 "slidehide" : true,
38096 * @event panelactivated
38097 * Fires when a panel is activated.
38098 * @param {Roo.LayoutRegion} this
38099 * @param {Roo.ContentPanel} panel The activated panel
38101 "panelactivated" : true,
38104 * Fires when the user resizes this region.
38105 * @param {Roo.LayoutRegion} this
38106 * @param {Number} newSize The new size (width for east/west, height for north/south)
38110 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38111 this.panels = new Roo.util.MixedCollection();
38112 this.panels.getKey = this.getPanelId.createDelegate(this);
38114 this.activePanel = null;
38115 // ensure listeners are added...
38117 if (config.listeners || config.events) {
38118 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38119 listeners : config.listeners || {},
38120 events : config.events || {}
38124 if(skipConfig !== true){
38125 this.applyConfig(config);
38129 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38131 getPanelId : function(p){
38135 applyConfig : function(config){
38136 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38137 this.config = config;
38142 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38143 * the width, for horizontal (north, south) the height.
38144 * @param {Number} newSize The new width or height
38146 resizeTo : function(newSize){
38147 var el = this.el ? this.el :
38148 (this.activePanel ? this.activePanel.getEl() : null);
38150 switch(this.position){
38153 el.setWidth(newSize);
38154 this.fireEvent("resized", this, newSize);
38158 el.setHeight(newSize);
38159 this.fireEvent("resized", this, newSize);
38165 getBox : function(){
38166 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38169 getMargins : function(){
38170 return this.margins;
38173 updateBox : function(box){
38175 var el = this.activePanel.getEl();
38176 el.dom.style.left = box.x + "px";
38177 el.dom.style.top = box.y + "px";
38178 this.activePanel.setSize(box.width, box.height);
38182 * Returns the container element for this region.
38183 * @return {Roo.Element}
38185 getEl : function(){
38186 return this.activePanel;
38190 * Returns true if this region is currently visible.
38191 * @return {Boolean}
38193 isVisible : function(){
38194 return this.activePanel ? true : false;
38197 setActivePanel : function(panel){
38198 panel = this.getPanel(panel);
38199 if(this.activePanel && this.activePanel != panel){
38200 this.activePanel.setActiveState(false);
38201 this.activePanel.getEl().setLeftTop(-10000,-10000);
38203 this.activePanel = panel;
38204 panel.setActiveState(true);
38206 panel.setSize(this.box.width, this.box.height);
38208 this.fireEvent("panelactivated", this, panel);
38209 this.fireEvent("invalidated");
38213 * Show the specified panel.
38214 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38215 * @return {Roo.ContentPanel} The shown panel or null
38217 showPanel : function(panel){
38218 panel = this.getPanel(panel);
38220 this.setActivePanel(panel);
38226 * Get the active panel for this region.
38227 * @return {Roo.ContentPanel} The active panel or null
38229 getActivePanel : function(){
38230 return this.activePanel;
38234 * Add the passed ContentPanel(s)
38235 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38236 * @return {Roo.ContentPanel} The panel added (if only one was added)
38238 add : function(panel){
38239 if(arguments.length > 1){
38240 for(var i = 0, len = arguments.length; i < len; i++) {
38241 this.add(arguments[i]);
38245 if(this.hasPanel(panel)){
38246 this.showPanel(panel);
38249 var el = panel.getEl();
38250 if(el.dom.parentNode != this.mgr.el.dom){
38251 this.mgr.el.dom.appendChild(el.dom);
38253 if(panel.setRegion){
38254 panel.setRegion(this);
38256 this.panels.add(panel);
38257 el.setStyle("position", "absolute");
38258 if(!panel.background){
38259 this.setActivePanel(panel);
38260 if(this.config.initialSize && this.panels.getCount()==1){
38261 this.resizeTo(this.config.initialSize);
38264 this.fireEvent("paneladded", this, panel);
38269 * Returns true if the panel is in this region.
38270 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38271 * @return {Boolean}
38273 hasPanel : function(panel){
38274 if(typeof panel == "object"){ // must be panel obj
38275 panel = panel.getId();
38277 return this.getPanel(panel) ? true : false;
38281 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38282 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38283 * @param {Boolean} preservePanel Overrides the config preservePanel option
38284 * @return {Roo.ContentPanel} The panel that was removed
38286 remove : function(panel, preservePanel){
38287 panel = this.getPanel(panel);
38292 this.fireEvent("beforeremove", this, panel, e);
38293 if(e.cancel === true){
38296 var panelId = panel.getId();
38297 this.panels.removeKey(panelId);
38302 * Returns the panel specified or null if it's not in this region.
38303 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38304 * @return {Roo.ContentPanel}
38306 getPanel : function(id){
38307 if(typeof id == "object"){ // must be panel obj
38310 return this.panels.get(id);
38314 * Returns this regions position (north/south/east/west/center).
38317 getPosition: function(){
38318 return this.position;
38322 * Ext JS Library 1.1.1
38323 * Copyright(c) 2006-2007, Ext JS, LLC.
38325 * Originally Released Under LGPL - original licence link has changed is not relivant.
38328 * <script type="text/javascript">
38332 * @class Roo.bootstrap.layout.Region
38333 * @extends Roo.bootstrap.layout.Basic
38334 * This class represents a region in a layout manager.
38336 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38337 * @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})
38338 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38339 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38340 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38341 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38342 * @cfg {String} title The title for the region (overrides panel titles)
38343 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38344 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38345 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38346 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38347 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38348 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38349 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38350 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38351 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38352 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38354 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38355 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38356 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38357 * @cfg {Number} width For East/West panels
38358 * @cfg {Number} height For North/South panels
38359 * @cfg {Boolean} split To show the splitter
38360 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38362 * @cfg {string} cls Extra CSS classes to add to region
38364 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38365 * @cfg {string} region the region that it inhabits..
38368 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38369 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38371 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38372 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38373 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38375 Roo.bootstrap.layout.Region = function(config)
38377 this.applyConfig(config);
38379 var mgr = config.mgr;
38380 var pos = config.region;
38381 config.skipConfig = true;
38382 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38385 this.onRender(mgr.el);
38388 this.visible = true;
38389 this.collapsed = false;
38390 this.unrendered_panels = [];
38393 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38395 position: '', // set by wrapper (eg. north/south etc..)
38396 unrendered_panels : null, // unrendered panels.
38398 tabPosition : false,
38400 mgr: false, // points to 'Border'
38403 createBody : function(){
38404 /** This region's body element
38405 * @type Roo.Element */
38406 this.bodyEl = this.el.createChild({
38408 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38412 onRender: function(ctr, pos)
38414 var dh = Roo.DomHelper;
38415 /** This region's container element
38416 * @type Roo.Element */
38417 this.el = dh.append(ctr.dom, {
38419 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38421 /** This region's title element
38422 * @type Roo.Element */
38424 this.titleEl = dh.append(this.el.dom, {
38426 unselectable: "on",
38427 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38429 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38430 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38434 this.titleEl.enableDisplayMode();
38435 /** This region's title text element
38436 * @type HTMLElement */
38437 this.titleTextEl = this.titleEl.dom.firstChild;
38438 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38440 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38441 this.closeBtn.enableDisplayMode();
38442 this.closeBtn.on("click", this.closeClicked, this);
38443 this.closeBtn.hide();
38445 this.createBody(this.config);
38446 if(this.config.hideWhenEmpty){
38448 this.on("paneladded", this.validateVisibility, this);
38449 this.on("panelremoved", this.validateVisibility, this);
38451 if(this.autoScroll){
38452 this.bodyEl.setStyle("overflow", "auto");
38454 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38456 //if(c.titlebar !== false){
38457 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38458 this.titleEl.hide();
38460 this.titleEl.show();
38461 if(this.config.title){
38462 this.titleTextEl.innerHTML = this.config.title;
38466 if(this.config.collapsed){
38467 this.collapse(true);
38469 if(this.config.hidden){
38473 if (this.unrendered_panels && this.unrendered_panels.length) {
38474 for (var i =0;i< this.unrendered_panels.length; i++) {
38475 this.add(this.unrendered_panels[i]);
38477 this.unrendered_panels = null;
38483 applyConfig : function(c)
38486 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38487 var dh = Roo.DomHelper;
38488 if(c.titlebar !== false){
38489 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38490 this.collapseBtn.on("click", this.collapse, this);
38491 this.collapseBtn.enableDisplayMode();
38493 if(c.showPin === true || this.showPin){
38494 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38495 this.stickBtn.enableDisplayMode();
38496 this.stickBtn.on("click", this.expand, this);
38497 this.stickBtn.hide();
38502 /** This region's collapsed element
38503 * @type Roo.Element */
38506 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38507 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38510 if(c.floatable !== false){
38511 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38512 this.collapsedEl.on("click", this.collapseClick, this);
38515 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38516 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38517 id: "message", unselectable: "on", style:{"float":"left"}});
38518 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38520 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38521 this.expandBtn.on("click", this.expand, this);
38525 if(this.collapseBtn){
38526 this.collapseBtn.setVisible(c.collapsible == true);
38529 this.cmargins = c.cmargins || this.cmargins ||
38530 (this.position == "west" || this.position == "east" ?
38531 {top: 0, left: 2, right:2, bottom: 0} :
38532 {top: 2, left: 0, right:0, bottom: 2});
38534 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38537 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38539 this.autoScroll = c.autoScroll || false;
38544 this.duration = c.duration || .30;
38545 this.slideDuration = c.slideDuration || .45;
38550 * Returns true if this region is currently visible.
38551 * @return {Boolean}
38553 isVisible : function(){
38554 return this.visible;
38558 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38559 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38561 //setCollapsedTitle : function(title){
38562 // title = title || " ";
38563 // if(this.collapsedTitleTextEl){
38564 // this.collapsedTitleTextEl.innerHTML = title;
38568 getBox : function(){
38570 // if(!this.collapsed){
38571 b = this.el.getBox(false, true);
38573 // b = this.collapsedEl.getBox(false, true);
38578 getMargins : function(){
38579 return this.margins;
38580 //return this.collapsed ? this.cmargins : this.margins;
38583 highlight : function(){
38584 this.el.addClass("x-layout-panel-dragover");
38587 unhighlight : function(){
38588 this.el.removeClass("x-layout-panel-dragover");
38591 updateBox : function(box)
38593 if (!this.bodyEl) {
38594 return; // not rendered yet..
38598 if(!this.collapsed){
38599 this.el.dom.style.left = box.x + "px";
38600 this.el.dom.style.top = box.y + "px";
38601 this.updateBody(box.width, box.height);
38603 this.collapsedEl.dom.style.left = box.x + "px";
38604 this.collapsedEl.dom.style.top = box.y + "px";
38605 this.collapsedEl.setSize(box.width, box.height);
38608 this.tabs.autoSizeTabs();
38612 updateBody : function(w, h)
38615 this.el.setWidth(w);
38616 w -= this.el.getBorderWidth("rl");
38617 if(this.config.adjustments){
38618 w += this.config.adjustments[0];
38621 if(h !== null && h > 0){
38622 this.el.setHeight(h);
38623 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38624 h -= this.el.getBorderWidth("tb");
38625 if(this.config.adjustments){
38626 h += this.config.adjustments[1];
38628 this.bodyEl.setHeight(h);
38630 h = this.tabs.syncHeight(h);
38633 if(this.panelSize){
38634 w = w !== null ? w : this.panelSize.width;
38635 h = h !== null ? h : this.panelSize.height;
38637 if(this.activePanel){
38638 var el = this.activePanel.getEl();
38639 w = w !== null ? w : el.getWidth();
38640 h = h !== null ? h : el.getHeight();
38641 this.panelSize = {width: w, height: h};
38642 this.activePanel.setSize(w, h);
38644 if(Roo.isIE && this.tabs){
38645 this.tabs.el.repaint();
38650 * Returns the container element for this region.
38651 * @return {Roo.Element}
38653 getEl : function(){
38658 * Hides this region.
38661 //if(!this.collapsed){
38662 this.el.dom.style.left = "-2000px";
38665 // this.collapsedEl.dom.style.left = "-2000px";
38666 // this.collapsedEl.hide();
38668 this.visible = false;
38669 this.fireEvent("visibilitychange", this, false);
38673 * Shows this region if it was previously hidden.
38676 //if(!this.collapsed){
38679 // this.collapsedEl.show();
38681 this.visible = true;
38682 this.fireEvent("visibilitychange", this, true);
38685 closeClicked : function(){
38686 if(this.activePanel){
38687 this.remove(this.activePanel);
38691 collapseClick : function(e){
38693 e.stopPropagation();
38696 e.stopPropagation();
38702 * Collapses this region.
38703 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38706 collapse : function(skipAnim, skipCheck = false){
38707 if(this.collapsed) {
38711 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38713 this.collapsed = true;
38715 this.split.el.hide();
38717 if(this.config.animate && skipAnim !== true){
38718 this.fireEvent("invalidated", this);
38719 this.animateCollapse();
38721 this.el.setLocation(-20000,-20000);
38723 this.collapsedEl.show();
38724 this.fireEvent("collapsed", this);
38725 this.fireEvent("invalidated", this);
38731 animateCollapse : function(){
38736 * Expands this region if it was previously collapsed.
38737 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38738 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38741 expand : function(e, skipAnim){
38743 e.stopPropagation();
38745 if(!this.collapsed || this.el.hasActiveFx()) {
38749 this.afterSlideIn();
38752 this.collapsed = false;
38753 if(this.config.animate && skipAnim !== true){
38754 this.animateExpand();
38758 this.split.el.show();
38760 this.collapsedEl.setLocation(-2000,-2000);
38761 this.collapsedEl.hide();
38762 this.fireEvent("invalidated", this);
38763 this.fireEvent("expanded", this);
38767 animateExpand : function(){
38771 initTabs : function()
38773 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38775 var ts = new Roo.bootstrap.panel.Tabs({
38776 el: this.bodyEl.dom,
38778 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38779 disableTooltips: this.config.disableTabTips,
38780 toolbar : this.config.toolbar
38783 if(this.config.hideTabs){
38784 ts.stripWrap.setDisplayed(false);
38787 ts.resizeTabs = this.config.resizeTabs === true;
38788 ts.minTabWidth = this.config.minTabWidth || 40;
38789 ts.maxTabWidth = this.config.maxTabWidth || 250;
38790 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38791 ts.monitorResize = false;
38792 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38793 ts.bodyEl.addClass('roo-layout-tabs-body');
38794 this.panels.each(this.initPanelAsTab, this);
38797 initPanelAsTab : function(panel){
38798 var ti = this.tabs.addTab(
38802 this.config.closeOnTab && panel.isClosable(),
38805 if(panel.tabTip !== undefined){
38806 ti.setTooltip(panel.tabTip);
38808 ti.on("activate", function(){
38809 this.setActivePanel(panel);
38812 if(this.config.closeOnTab){
38813 ti.on("beforeclose", function(t, e){
38815 this.remove(panel);
38819 panel.tabItem = ti;
38824 updatePanelTitle : function(panel, title)
38826 if(this.activePanel == panel){
38827 this.updateTitle(title);
38830 var ti = this.tabs.getTab(panel.getEl().id);
38832 if(panel.tabTip !== undefined){
38833 ti.setTooltip(panel.tabTip);
38838 updateTitle : function(title){
38839 if(this.titleTextEl && !this.config.title){
38840 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38844 setActivePanel : function(panel)
38846 panel = this.getPanel(panel);
38847 if(this.activePanel && this.activePanel != panel){
38848 if(this.activePanel.setActiveState(false) === false){
38852 this.activePanel = panel;
38853 panel.setActiveState(true);
38854 if(this.panelSize){
38855 panel.setSize(this.panelSize.width, this.panelSize.height);
38858 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38860 this.updateTitle(panel.getTitle());
38862 this.fireEvent("invalidated", this);
38864 this.fireEvent("panelactivated", this, panel);
38868 * Shows the specified panel.
38869 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38870 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38872 showPanel : function(panel)
38874 panel = this.getPanel(panel);
38877 var tab = this.tabs.getTab(panel.getEl().id);
38878 if(tab.isHidden()){
38879 this.tabs.unhideTab(tab.id);
38883 this.setActivePanel(panel);
38890 * Get the active panel for this region.
38891 * @return {Roo.ContentPanel} The active panel or null
38893 getActivePanel : function(){
38894 return this.activePanel;
38897 validateVisibility : function(){
38898 if(this.panels.getCount() < 1){
38899 this.updateTitle(" ");
38900 this.closeBtn.hide();
38903 if(!this.isVisible()){
38910 * Adds the passed ContentPanel(s) to this region.
38911 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38912 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38914 add : function(panel)
38916 if(arguments.length > 1){
38917 for(var i = 0, len = arguments.length; i < len; i++) {
38918 this.add(arguments[i]);
38923 // if we have not been rendered yet, then we can not really do much of this..
38924 if (!this.bodyEl) {
38925 this.unrendered_panels.push(panel);
38932 if(this.hasPanel(panel)){
38933 this.showPanel(panel);
38936 panel.setRegion(this);
38937 this.panels.add(panel);
38938 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38939 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38940 // and hide them... ???
38941 this.bodyEl.dom.appendChild(panel.getEl().dom);
38942 if(panel.background !== true){
38943 this.setActivePanel(panel);
38945 this.fireEvent("paneladded", this, panel);
38952 this.initPanelAsTab(panel);
38956 if(panel.background !== true){
38957 this.tabs.activate(panel.getEl().id);
38959 this.fireEvent("paneladded", this, panel);
38964 * Hides the tab for the specified panel.
38965 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38967 hidePanel : function(panel){
38968 if(this.tabs && (panel = this.getPanel(panel))){
38969 this.tabs.hideTab(panel.getEl().id);
38974 * Unhides the tab for a previously hidden panel.
38975 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38977 unhidePanel : function(panel){
38978 if(this.tabs && (panel = this.getPanel(panel))){
38979 this.tabs.unhideTab(panel.getEl().id);
38983 clearPanels : function(){
38984 while(this.panels.getCount() > 0){
38985 this.remove(this.panels.first());
38990 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38991 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38992 * @param {Boolean} preservePanel Overrides the config preservePanel option
38993 * @return {Roo.ContentPanel} The panel that was removed
38995 remove : function(panel, preservePanel)
38997 panel = this.getPanel(panel);
39002 this.fireEvent("beforeremove", this, panel, e);
39003 if(e.cancel === true){
39006 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39007 var panelId = panel.getId();
39008 this.panels.removeKey(panelId);
39010 document.body.appendChild(panel.getEl().dom);
39013 this.tabs.removeTab(panel.getEl().id);
39014 }else if (!preservePanel){
39015 this.bodyEl.dom.removeChild(panel.getEl().dom);
39017 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39018 var p = this.panels.first();
39019 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39020 tempEl.appendChild(p.getEl().dom);
39021 this.bodyEl.update("");
39022 this.bodyEl.dom.appendChild(p.getEl().dom);
39024 this.updateTitle(p.getTitle());
39026 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39027 this.setActivePanel(p);
39029 panel.setRegion(null);
39030 if(this.activePanel == panel){
39031 this.activePanel = null;
39033 if(this.config.autoDestroy !== false && preservePanel !== true){
39034 try{panel.destroy();}catch(e){}
39036 this.fireEvent("panelremoved", this, panel);
39041 * Returns the TabPanel component used by this region
39042 * @return {Roo.TabPanel}
39044 getTabs : function(){
39048 createTool : function(parentEl, className){
39049 var btn = Roo.DomHelper.append(parentEl, {
39051 cls: "x-layout-tools-button",
39054 cls: "roo-layout-tools-button-inner " + className,
39058 btn.addClassOnOver("roo-layout-tools-button-over");
39063 * Ext JS Library 1.1.1
39064 * Copyright(c) 2006-2007, Ext JS, LLC.
39066 * Originally Released Under LGPL - original licence link has changed is not relivant.
39069 * <script type="text/javascript">
39075 * @class Roo.SplitLayoutRegion
39076 * @extends Roo.LayoutRegion
39077 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39079 Roo.bootstrap.layout.Split = function(config){
39080 this.cursor = config.cursor;
39081 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39084 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39086 splitTip : "Drag to resize.",
39087 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39088 useSplitTips : false,
39090 applyConfig : function(config){
39091 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39094 onRender : function(ctr,pos) {
39096 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39097 if(!this.config.split){
39102 var splitEl = Roo.DomHelper.append(ctr.dom, {
39104 id: this.el.id + "-split",
39105 cls: "roo-layout-split roo-layout-split-"+this.position,
39108 /** The SplitBar for this region
39109 * @type Roo.SplitBar */
39110 // does not exist yet...
39111 Roo.log([this.position, this.orientation]);
39113 this.split = new Roo.bootstrap.SplitBar({
39114 dragElement : splitEl,
39115 resizingElement: this.el,
39116 orientation : this.orientation
39119 this.split.on("moved", this.onSplitMove, this);
39120 this.split.useShim = this.config.useShim === true;
39121 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39122 if(this.useSplitTips){
39123 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39125 //if(config.collapsible){
39126 // this.split.el.on("dblclick", this.collapse, this);
39129 if(typeof this.config.minSize != "undefined"){
39130 this.split.minSize = this.config.minSize;
39132 if(typeof this.config.maxSize != "undefined"){
39133 this.split.maxSize = this.config.maxSize;
39135 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39136 this.hideSplitter();
39141 getHMaxSize : function(){
39142 var cmax = this.config.maxSize || 10000;
39143 var center = this.mgr.getRegion("center");
39144 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39147 getVMaxSize : function(){
39148 var cmax = this.config.maxSize || 10000;
39149 var center = this.mgr.getRegion("center");
39150 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39153 onSplitMove : function(split, newSize){
39154 this.fireEvent("resized", this, newSize);
39158 * Returns the {@link Roo.SplitBar} for this region.
39159 * @return {Roo.SplitBar}
39161 getSplitBar : function(){
39166 this.hideSplitter();
39167 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39170 hideSplitter : function(){
39172 this.split.el.setLocation(-2000,-2000);
39173 this.split.el.hide();
39179 this.split.el.show();
39181 Roo.bootstrap.layout.Split.superclass.show.call(this);
39184 beforeSlide: function(){
39185 if(Roo.isGecko){// firefox overflow auto bug workaround
39186 this.bodyEl.clip();
39188 this.tabs.bodyEl.clip();
39190 if(this.activePanel){
39191 this.activePanel.getEl().clip();
39193 if(this.activePanel.beforeSlide){
39194 this.activePanel.beforeSlide();
39200 afterSlide : function(){
39201 if(Roo.isGecko){// firefox overflow auto bug workaround
39202 this.bodyEl.unclip();
39204 this.tabs.bodyEl.unclip();
39206 if(this.activePanel){
39207 this.activePanel.getEl().unclip();
39208 if(this.activePanel.afterSlide){
39209 this.activePanel.afterSlide();
39215 initAutoHide : function(){
39216 if(this.autoHide !== false){
39217 if(!this.autoHideHd){
39218 var st = new Roo.util.DelayedTask(this.slideIn, this);
39219 this.autoHideHd = {
39220 "mouseout": function(e){
39221 if(!e.within(this.el, true)){
39225 "mouseover" : function(e){
39231 this.el.on(this.autoHideHd);
39235 clearAutoHide : function(){
39236 if(this.autoHide !== false){
39237 this.el.un("mouseout", this.autoHideHd.mouseout);
39238 this.el.un("mouseover", this.autoHideHd.mouseover);
39242 clearMonitor : function(){
39243 Roo.get(document).un("click", this.slideInIf, this);
39246 // these names are backwards but not changed for compat
39247 slideOut : function(){
39248 if(this.isSlid || this.el.hasActiveFx()){
39251 this.isSlid = true;
39252 if(this.collapseBtn){
39253 this.collapseBtn.hide();
39255 this.closeBtnState = this.closeBtn.getStyle('display');
39256 this.closeBtn.hide();
39258 this.stickBtn.show();
39261 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39262 this.beforeSlide();
39263 this.el.setStyle("z-index", 10001);
39264 this.el.slideIn(this.getSlideAnchor(), {
39265 callback: function(){
39267 this.initAutoHide();
39268 Roo.get(document).on("click", this.slideInIf, this);
39269 this.fireEvent("slideshow", this);
39276 afterSlideIn : function(){
39277 this.clearAutoHide();
39278 this.isSlid = false;
39279 this.clearMonitor();
39280 this.el.setStyle("z-index", "");
39281 if(this.collapseBtn){
39282 this.collapseBtn.show();
39284 this.closeBtn.setStyle('display', this.closeBtnState);
39286 this.stickBtn.hide();
39288 this.fireEvent("slidehide", this);
39291 slideIn : function(cb){
39292 if(!this.isSlid || this.el.hasActiveFx()){
39296 this.isSlid = false;
39297 this.beforeSlide();
39298 this.el.slideOut(this.getSlideAnchor(), {
39299 callback: function(){
39300 this.el.setLeftTop(-10000, -10000);
39302 this.afterSlideIn();
39310 slideInIf : function(e){
39311 if(!e.within(this.el)){
39316 animateCollapse : function(){
39317 this.beforeSlide();
39318 this.el.setStyle("z-index", 20000);
39319 var anchor = this.getSlideAnchor();
39320 this.el.slideOut(anchor, {
39321 callback : function(){
39322 this.el.setStyle("z-index", "");
39323 this.collapsedEl.slideIn(anchor, {duration:.3});
39325 this.el.setLocation(-10000,-10000);
39327 this.fireEvent("collapsed", this);
39334 animateExpand : function(){
39335 this.beforeSlide();
39336 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39337 this.el.setStyle("z-index", 20000);
39338 this.collapsedEl.hide({
39341 this.el.slideIn(this.getSlideAnchor(), {
39342 callback : function(){
39343 this.el.setStyle("z-index", "");
39346 this.split.el.show();
39348 this.fireEvent("invalidated", this);
39349 this.fireEvent("expanded", this);
39377 getAnchor : function(){
39378 return this.anchors[this.position];
39381 getCollapseAnchor : function(){
39382 return this.canchors[this.position];
39385 getSlideAnchor : function(){
39386 return this.sanchors[this.position];
39389 getAlignAdj : function(){
39390 var cm = this.cmargins;
39391 switch(this.position){
39407 getExpandAdj : function(){
39408 var c = this.collapsedEl, cm = this.cmargins;
39409 switch(this.position){
39411 return [-(cm.right+c.getWidth()+cm.left), 0];
39414 return [cm.right+c.getWidth()+cm.left, 0];
39417 return [0, -(cm.top+cm.bottom+c.getHeight())];
39420 return [0, cm.top+cm.bottom+c.getHeight()];
39426 * Ext JS Library 1.1.1
39427 * Copyright(c) 2006-2007, Ext JS, LLC.
39429 * Originally Released Under LGPL - original licence link has changed is not relivant.
39432 * <script type="text/javascript">
39435 * These classes are private internal classes
39437 Roo.bootstrap.layout.Center = function(config){
39438 config.region = "center";
39439 Roo.bootstrap.layout.Region.call(this, config);
39440 this.visible = true;
39441 this.minWidth = config.minWidth || 20;
39442 this.minHeight = config.minHeight || 20;
39445 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39447 // center panel can't be hidden
39451 // center panel can't be hidden
39454 getMinWidth: function(){
39455 return this.minWidth;
39458 getMinHeight: function(){
39459 return this.minHeight;
39473 Roo.bootstrap.layout.North = function(config)
39475 config.region = 'north';
39476 config.cursor = 'n-resize';
39478 Roo.bootstrap.layout.Split.call(this, config);
39482 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39483 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39484 this.split.el.addClass("roo-layout-split-v");
39486 //var size = config.initialSize || config.height;
39487 //if(this.el && typeof size != "undefined"){
39488 // this.el.setHeight(size);
39491 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39493 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39496 onRender : function(ctr, pos)
39498 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39499 var size = this.config.initialSize || this.config.height;
39500 if(this.el && typeof size != "undefined"){
39501 this.el.setHeight(size);
39506 getBox : function(){
39507 if(this.collapsed){
39508 return this.collapsedEl.getBox();
39510 var box = this.el.getBox();
39512 box.height += this.split.el.getHeight();
39517 updateBox : function(box){
39518 if(this.split && !this.collapsed){
39519 box.height -= this.split.el.getHeight();
39520 this.split.el.setLeft(box.x);
39521 this.split.el.setTop(box.y+box.height);
39522 this.split.el.setWidth(box.width);
39524 if(this.collapsed){
39525 this.updateBody(box.width, null);
39527 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39535 Roo.bootstrap.layout.South = function(config){
39536 config.region = 'south';
39537 config.cursor = 's-resize';
39538 Roo.bootstrap.layout.Split.call(this, config);
39540 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39541 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39542 this.split.el.addClass("roo-layout-split-v");
39547 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39548 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39550 onRender : function(ctr, pos)
39552 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39553 var size = this.config.initialSize || this.config.height;
39554 if(this.el && typeof size != "undefined"){
39555 this.el.setHeight(size);
39560 getBox : function(){
39561 if(this.collapsed){
39562 return this.collapsedEl.getBox();
39564 var box = this.el.getBox();
39566 var sh = this.split.el.getHeight();
39573 updateBox : function(box){
39574 if(this.split && !this.collapsed){
39575 var sh = this.split.el.getHeight();
39578 this.split.el.setLeft(box.x);
39579 this.split.el.setTop(box.y-sh);
39580 this.split.el.setWidth(box.width);
39582 if(this.collapsed){
39583 this.updateBody(box.width, null);
39585 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39589 Roo.bootstrap.layout.East = function(config){
39590 config.region = "east";
39591 config.cursor = "e-resize";
39592 Roo.bootstrap.layout.Split.call(this, config);
39594 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39595 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39596 this.split.el.addClass("roo-layout-split-h");
39600 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39601 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39603 onRender : function(ctr, pos)
39605 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39606 var size = this.config.initialSize || this.config.width;
39607 if(this.el && typeof size != "undefined"){
39608 this.el.setWidth(size);
39613 getBox : function(){
39614 if(this.collapsed){
39615 return this.collapsedEl.getBox();
39617 var box = this.el.getBox();
39619 var sw = this.split.el.getWidth();
39626 updateBox : function(box){
39627 if(this.split && !this.collapsed){
39628 var sw = this.split.el.getWidth();
39630 this.split.el.setLeft(box.x);
39631 this.split.el.setTop(box.y);
39632 this.split.el.setHeight(box.height);
39635 if(this.collapsed){
39636 this.updateBody(null, box.height);
39638 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39642 Roo.bootstrap.layout.West = function(config){
39643 config.region = "west";
39644 config.cursor = "w-resize";
39646 Roo.bootstrap.layout.Split.call(this, config);
39648 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39649 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39650 this.split.el.addClass("roo-layout-split-h");
39654 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39655 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39657 onRender: function(ctr, pos)
39659 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39660 var size = this.config.initialSize || this.config.width;
39661 if(typeof size != "undefined"){
39662 this.el.setWidth(size);
39666 getBox : function(){
39667 if(this.collapsed){
39668 return this.collapsedEl.getBox();
39670 var box = this.el.getBox();
39671 if (box.width == 0) {
39672 box.width = this.config.width; // kludge?
39675 box.width += this.split.el.getWidth();
39680 updateBox : function(box){
39681 if(this.split && !this.collapsed){
39682 var sw = this.split.el.getWidth();
39684 this.split.el.setLeft(box.x+box.width);
39685 this.split.el.setTop(box.y);
39686 this.split.el.setHeight(box.height);
39688 if(this.collapsed){
39689 this.updateBody(null, box.height);
39691 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39693 });Roo.namespace("Roo.bootstrap.panel");/*
39695 * Ext JS Library 1.1.1
39696 * Copyright(c) 2006-2007, Ext JS, LLC.
39698 * Originally Released Under LGPL - original licence link has changed is not relivant.
39701 * <script type="text/javascript">
39704 * @class Roo.ContentPanel
39705 * @extends Roo.util.Observable
39706 * A basic ContentPanel element.
39707 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39708 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39709 * @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
39710 * @cfg {Boolean} closable True if the panel can be closed/removed
39711 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39712 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39713 * @cfg {Toolbar} toolbar A toolbar for this panel
39714 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39715 * @cfg {String} title The title for this panel
39716 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39717 * @cfg {String} url Calls {@link #setUrl} with this value
39718 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39719 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39720 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39721 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39722 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39723 * @cfg {Boolean} badges render the badges
39724 * @cfg {String} cls extra classes to use
39725 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39728 * Create a new ContentPanel.
39729 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39730 * @param {String/Object} config A string to set only the title or a config object
39731 * @param {String} content (optional) Set the HTML content for this panel
39732 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39734 Roo.bootstrap.panel.Content = function( config){
39736 this.tpl = config.tpl || false;
39738 var el = config.el;
39739 var content = config.content;
39741 if(config.autoCreate){ // xtype is available if this is called from factory
39744 this.el = Roo.get(el);
39745 if(!this.el && config && config.autoCreate){
39746 if(typeof config.autoCreate == "object"){
39747 if(!config.autoCreate.id){
39748 config.autoCreate.id = config.id||el;
39750 this.el = Roo.DomHelper.append(document.body,
39751 config.autoCreate, true);
39755 cls: (config.cls || '') +
39756 (config.background ? ' bg-' + config.background : '') +
39757 " roo-layout-inactive-content",
39760 if (config.iframe) {
39764 style : 'border: 0px',
39765 src : 'about:blank'
39771 elcfg.html = config.html;
39775 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39776 if (config.iframe) {
39777 this.iframeEl = this.el.select('iframe',true).first();
39782 this.closable = false;
39783 this.loaded = false;
39784 this.active = false;
39787 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39789 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39791 this.wrapEl = this.el; //this.el.wrap();
39793 if (config.toolbar.items) {
39794 ti = config.toolbar.items ;
39795 delete config.toolbar.items ;
39799 this.toolbar.render(this.wrapEl, 'before');
39800 for(var i =0;i < ti.length;i++) {
39801 // Roo.log(['add child', items[i]]);
39802 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39804 this.toolbar.items = nitems;
39805 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39806 delete config.toolbar;
39810 // xtype created footer. - not sure if will work as we normally have to render first..
39811 if (this.footer && !this.footer.el && this.footer.xtype) {
39812 if (!this.wrapEl) {
39813 this.wrapEl = this.el.wrap();
39816 this.footer.container = this.wrapEl.createChild();
39818 this.footer = Roo.factory(this.footer, Roo);
39823 if(typeof config == "string"){
39824 this.title = config;
39826 Roo.apply(this, config);
39830 this.resizeEl = Roo.get(this.resizeEl, true);
39832 this.resizeEl = this.el;
39834 // handle view.xtype
39842 * Fires when this panel is activated.
39843 * @param {Roo.ContentPanel} this
39847 * @event deactivate
39848 * Fires when this panel is activated.
39849 * @param {Roo.ContentPanel} this
39851 "deactivate" : true,
39855 * Fires when this panel is resized if fitToFrame is true.
39856 * @param {Roo.ContentPanel} this
39857 * @param {Number} width The width after any component adjustments
39858 * @param {Number} height The height after any component adjustments
39864 * Fires when this tab is created
39865 * @param {Roo.ContentPanel} this
39876 if(this.autoScroll && !this.iframe){
39877 this.resizeEl.setStyle("overflow", "auto");
39879 // fix randome scrolling
39880 //this.el.on('scroll', function() {
39881 // Roo.log('fix random scolling');
39882 // this.scrollTo('top',0);
39885 content = content || this.content;
39887 this.setContent(content);
39889 if(config && config.url){
39890 this.setUrl(this.url, this.params, this.loadOnce);
39895 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39897 if (this.view && typeof(this.view.xtype) != 'undefined') {
39898 this.view.el = this.el.appendChild(document.createElement("div"));
39899 this.view = Roo.factory(this.view);
39900 this.view.render && this.view.render(false, '');
39904 this.fireEvent('render', this);
39907 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39917 setRegion : function(region){
39918 this.region = region;
39919 this.setActiveClass(region && !this.background);
39923 setActiveClass: function(state)
39926 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39927 this.el.setStyle('position','relative');
39929 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39930 this.el.setStyle('position', 'absolute');
39935 * Returns the toolbar for this Panel if one was configured.
39936 * @return {Roo.Toolbar}
39938 getToolbar : function(){
39939 return this.toolbar;
39942 setActiveState : function(active)
39944 this.active = active;
39945 this.setActiveClass(active);
39947 if(this.fireEvent("deactivate", this) === false){
39952 this.fireEvent("activate", this);
39956 * Updates this panel's element (not for iframe)
39957 * @param {String} content The new content
39958 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39960 setContent : function(content, loadScripts){
39965 this.el.update(content, loadScripts);
39968 ignoreResize : function(w, h){
39969 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39972 this.lastSize = {width: w, height: h};
39977 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39978 * @return {Roo.UpdateManager} The UpdateManager
39980 getUpdateManager : function(){
39984 return this.el.getUpdateManager();
39987 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39988 * Does not work with IFRAME contents
39989 * @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:
39992 url: "your-url.php",
39993 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39994 callback: yourFunction,
39995 scope: yourObject, //(optional scope)
39998 text: "Loading...",
40004 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40005 * 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.
40006 * @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}
40007 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40008 * @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.
40009 * @return {Roo.ContentPanel} this
40017 var um = this.el.getUpdateManager();
40018 um.update.apply(um, arguments);
40024 * 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.
40025 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40026 * @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)
40027 * @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)
40028 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40030 setUrl : function(url, params, loadOnce){
40032 this.iframeEl.dom.src = url;
40036 if(this.refreshDelegate){
40037 this.removeListener("activate", this.refreshDelegate);
40039 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40040 this.on("activate", this.refreshDelegate);
40041 return this.el.getUpdateManager();
40044 _handleRefresh : function(url, params, loadOnce){
40045 if(!loadOnce || !this.loaded){
40046 var updater = this.el.getUpdateManager();
40047 updater.update(url, params, this._setLoaded.createDelegate(this));
40051 _setLoaded : function(){
40052 this.loaded = true;
40056 * Returns this panel's id
40059 getId : function(){
40064 * Returns this panel's element - used by regiosn to add.
40065 * @return {Roo.Element}
40067 getEl : function(){
40068 return this.wrapEl || this.el;
40073 adjustForComponents : function(width, height)
40075 //Roo.log('adjustForComponents ');
40076 if(this.resizeEl != this.el){
40077 width -= this.el.getFrameWidth('lr');
40078 height -= this.el.getFrameWidth('tb');
40081 var te = this.toolbar.getEl();
40082 te.setWidth(width);
40083 height -= te.getHeight();
40086 var te = this.footer.getEl();
40087 te.setWidth(width);
40088 height -= te.getHeight();
40092 if(this.adjustments){
40093 width += this.adjustments[0];
40094 height += this.adjustments[1];
40096 return {"width": width, "height": height};
40099 setSize : function(width, height){
40100 if(this.fitToFrame && !this.ignoreResize(width, height)){
40101 if(this.fitContainer && this.resizeEl != this.el){
40102 this.el.setSize(width, height);
40104 var size = this.adjustForComponents(width, height);
40106 this.iframeEl.setSize(width,height);
40109 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40110 this.fireEvent('resize', this, size.width, size.height);
40117 * Returns this panel's title
40120 getTitle : function(){
40122 if (typeof(this.title) != 'object') {
40127 for (var k in this.title) {
40128 if (!this.title.hasOwnProperty(k)) {
40132 if (k.indexOf('-') >= 0) {
40133 var s = k.split('-');
40134 for (var i = 0; i<s.length; i++) {
40135 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40138 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40145 * Set this panel's title
40146 * @param {String} title
40148 setTitle : function(title){
40149 this.title = title;
40151 this.region.updatePanelTitle(this, title);
40156 * Returns true is this panel was configured to be closable
40157 * @return {Boolean}
40159 isClosable : function(){
40160 return this.closable;
40163 beforeSlide : function(){
40165 this.resizeEl.clip();
40168 afterSlide : function(){
40170 this.resizeEl.unclip();
40174 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40175 * Will fail silently if the {@link #setUrl} method has not been called.
40176 * This does not activate the panel, just updates its content.
40178 refresh : function(){
40179 if(this.refreshDelegate){
40180 this.loaded = false;
40181 this.refreshDelegate();
40186 * Destroys this panel
40188 destroy : function(){
40189 this.el.removeAllListeners();
40190 var tempEl = document.createElement("span");
40191 tempEl.appendChild(this.el.dom);
40192 tempEl.innerHTML = "";
40198 * form - if the content panel contains a form - this is a reference to it.
40199 * @type {Roo.form.Form}
40203 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40204 * This contains a reference to it.
40210 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40220 * @param {Object} cfg Xtype definition of item to add.
40224 getChildContainer: function () {
40225 return this.getEl();
40230 var ret = new Roo.factory(cfg);
40235 if (cfg.xtype.match(/^Form$/)) {
40238 //if (this.footer) {
40239 // el = this.footer.container.insertSibling(false, 'before');
40241 el = this.el.createChild();
40244 this.form = new Roo.form.Form(cfg);
40247 if ( this.form.allItems.length) {
40248 this.form.render(el.dom);
40252 // should only have one of theses..
40253 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40254 // views.. should not be just added - used named prop 'view''
40256 cfg.el = this.el.appendChild(document.createElement("div"));
40259 var ret = new Roo.factory(cfg);
40261 ret.render && ret.render(false, ''); // render blank..
40271 * @class Roo.bootstrap.panel.Grid
40272 * @extends Roo.bootstrap.panel.Content
40274 * Create a new GridPanel.
40275 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40276 * @param {Object} config A the config object
40282 Roo.bootstrap.panel.Grid = function(config)
40286 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40287 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40289 config.el = this.wrapper;
40290 //this.el = this.wrapper;
40292 if (config.container) {
40293 // ctor'ed from a Border/panel.grid
40296 this.wrapper.setStyle("overflow", "hidden");
40297 this.wrapper.addClass('roo-grid-container');
40302 if(config.toolbar){
40303 var tool_el = this.wrapper.createChild();
40304 this.toolbar = Roo.factory(config.toolbar);
40306 if (config.toolbar.items) {
40307 ti = config.toolbar.items ;
40308 delete config.toolbar.items ;
40312 this.toolbar.render(tool_el);
40313 for(var i =0;i < ti.length;i++) {
40314 // Roo.log(['add child', items[i]]);
40315 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40317 this.toolbar.items = nitems;
40319 delete config.toolbar;
40322 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40323 config.grid.scrollBody = true;;
40324 config.grid.monitorWindowResize = false; // turn off autosizing
40325 config.grid.autoHeight = false;
40326 config.grid.autoWidth = false;
40328 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40330 if (config.background) {
40331 // render grid on panel activation (if panel background)
40332 this.on('activate', function(gp) {
40333 if (!gp.grid.rendered) {
40334 gp.grid.render(this.wrapper);
40335 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40340 this.grid.render(this.wrapper);
40341 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40344 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40345 // ??? needed ??? config.el = this.wrapper;
40350 // xtype created footer. - not sure if will work as we normally have to render first..
40351 if (this.footer && !this.footer.el && this.footer.xtype) {
40353 var ctr = this.grid.getView().getFooterPanel(true);
40354 this.footer.dataSource = this.grid.dataSource;
40355 this.footer = Roo.factory(this.footer, Roo);
40356 this.footer.render(ctr);
40366 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40367 getId : function(){
40368 return this.grid.id;
40372 * Returns the grid for this panel
40373 * @return {Roo.bootstrap.Table}
40375 getGrid : function(){
40379 setSize : function(width, height){
40380 if(!this.ignoreResize(width, height)){
40381 var grid = this.grid;
40382 var size = this.adjustForComponents(width, height);
40383 // tfoot is not a footer?
40386 var gridel = grid.getGridEl();
40387 gridel.setSize(size.width, size.height);
40389 var tbd = grid.getGridEl().select('tbody', true).first();
40390 var thd = grid.getGridEl().select('thead',true).first();
40391 var tbf= grid.getGridEl().select('tfoot', true).first();
40394 size.height -= tbf.getHeight();
40397 size.height -= thd.getHeight();
40400 tbd.setSize(size.width, size.height );
40401 // this is for the account management tab -seems to work there.
40402 var thd = grid.getGridEl().select('thead',true).first();
40404 // tbd.setSize(size.width, size.height - thd.getHeight());
40413 beforeSlide : function(){
40414 this.grid.getView().scroller.clip();
40417 afterSlide : function(){
40418 this.grid.getView().scroller.unclip();
40421 destroy : function(){
40422 this.grid.destroy();
40424 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40429 * @class Roo.bootstrap.panel.Nest
40430 * @extends Roo.bootstrap.panel.Content
40432 * Create a new Panel, that can contain a layout.Border.
40435 * @param {Roo.BorderLayout} layout The layout for this panel
40436 * @param {String/Object} config A string to set only the title or a config object
40438 Roo.bootstrap.panel.Nest = function(config)
40440 // construct with only one argument..
40441 /* FIXME - implement nicer consturctors
40442 if (layout.layout) {
40444 layout = config.layout;
40445 delete config.layout;
40447 if (layout.xtype && !layout.getEl) {
40448 // then layout needs constructing..
40449 layout = Roo.factory(layout, Roo);
40453 config.el = config.layout.getEl();
40455 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40457 config.layout.monitorWindowResize = false; // turn off autosizing
40458 this.layout = config.layout;
40459 this.layout.getEl().addClass("roo-layout-nested-layout");
40460 this.layout.parent = this;
40467 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40469 setSize : function(width, height){
40470 if(!this.ignoreResize(width, height)){
40471 var size = this.adjustForComponents(width, height);
40472 var el = this.layout.getEl();
40473 if (size.height < 1) {
40474 el.setWidth(size.width);
40476 el.setSize(size.width, size.height);
40478 var touch = el.dom.offsetWidth;
40479 this.layout.layout();
40480 // ie requires a double layout on the first pass
40481 if(Roo.isIE && !this.initialized){
40482 this.initialized = true;
40483 this.layout.layout();
40488 // activate all subpanels if not currently active..
40490 setActiveState : function(active){
40491 this.active = active;
40492 this.setActiveClass(active);
40495 this.fireEvent("deactivate", this);
40499 this.fireEvent("activate", this);
40500 // not sure if this should happen before or after..
40501 if (!this.layout) {
40502 return; // should not happen..
40505 for (var r in this.layout.regions) {
40506 reg = this.layout.getRegion(r);
40507 if (reg.getActivePanel()) {
40508 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40509 reg.setActivePanel(reg.getActivePanel());
40512 if (!reg.panels.length) {
40515 reg.showPanel(reg.getPanel(0));
40524 * Returns the nested BorderLayout for this panel
40525 * @return {Roo.BorderLayout}
40527 getLayout : function(){
40528 return this.layout;
40532 * Adds a xtype elements to the layout of the nested panel
40536 xtype : 'ContentPanel',
40543 xtype : 'NestedLayoutPanel',
40549 items : [ ... list of content panels or nested layout panels.. ]
40553 * @param {Object} cfg Xtype definition of item to add.
40555 addxtype : function(cfg) {
40556 return this.layout.addxtype(cfg);
40561 * Ext JS Library 1.1.1
40562 * Copyright(c) 2006-2007, Ext JS, LLC.
40564 * Originally Released Under LGPL - original licence link has changed is not relivant.
40567 * <script type="text/javascript">
40570 * @class Roo.TabPanel
40571 * @extends Roo.util.Observable
40572 * A lightweight tab container.
40576 // basic tabs 1, built from existing content
40577 var tabs = new Roo.TabPanel("tabs1");
40578 tabs.addTab("script", "View Script");
40579 tabs.addTab("markup", "View Markup");
40580 tabs.activate("script");
40582 // more advanced tabs, built from javascript
40583 var jtabs = new Roo.TabPanel("jtabs");
40584 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40586 // set up the UpdateManager
40587 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40588 var updater = tab2.getUpdateManager();
40589 updater.setDefaultUrl("ajax1.htm");
40590 tab2.on('activate', updater.refresh, updater, true);
40592 // Use setUrl for Ajax loading
40593 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40594 tab3.setUrl("ajax2.htm", null, true);
40597 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40600 jtabs.activate("jtabs-1");
40603 * Create a new TabPanel.
40604 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40605 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40607 Roo.bootstrap.panel.Tabs = function(config){
40609 * The container element for this TabPanel.
40610 * @type Roo.Element
40612 this.el = Roo.get(config.el);
40615 if(typeof config == "boolean"){
40616 this.tabPosition = config ? "bottom" : "top";
40618 Roo.apply(this, config);
40622 if(this.tabPosition == "bottom"){
40623 // if tabs are at the bottom = create the body first.
40624 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40625 this.el.addClass("roo-tabs-bottom");
40627 // next create the tabs holders
40629 if (this.tabPosition == "west"){
40631 var reg = this.region; // fake it..
40633 if (!reg.mgr.parent) {
40636 reg = reg.mgr.parent.region;
40638 Roo.log("got nest?");
40640 if (reg.mgr.getRegion('west')) {
40641 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40642 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40643 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40644 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40645 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40653 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40654 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40655 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40656 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40661 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40664 // finally - if tabs are at the top, then create the body last..
40665 if(this.tabPosition != "bottom"){
40666 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40667 * @type Roo.Element
40669 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40670 this.el.addClass("roo-tabs-top");
40674 this.bodyEl.setStyle("position", "relative");
40676 this.active = null;
40677 this.activateDelegate = this.activate.createDelegate(this);
40682 * Fires when the active tab changes
40683 * @param {Roo.TabPanel} this
40684 * @param {Roo.TabPanelItem} activePanel The new active tab
40688 * @event beforetabchange
40689 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40690 * @param {Roo.TabPanel} this
40691 * @param {Object} e Set cancel to true on this object to cancel the tab change
40692 * @param {Roo.TabPanelItem} tab The tab being changed to
40694 "beforetabchange" : true
40697 Roo.EventManager.onWindowResize(this.onResize, this);
40698 this.cpad = this.el.getPadding("lr");
40699 this.hiddenCount = 0;
40702 // toolbar on the tabbar support...
40703 if (this.toolbar) {
40704 alert("no toolbar support yet");
40705 this.toolbar = false;
40707 var tcfg = this.toolbar;
40708 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40709 this.toolbar = new Roo.Toolbar(tcfg);
40710 if (Roo.isSafari) {
40711 var tbl = tcfg.container.child('table', true);
40712 tbl.setAttribute('width', '100%');
40720 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40723 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40725 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40727 tabPosition : "top",
40729 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40731 currentTabWidth : 0,
40733 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40737 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40741 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40743 preferredTabWidth : 175,
40745 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40747 resizeTabs : false,
40749 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40751 monitorResize : true,
40753 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40755 toolbar : false, // set by caller..
40757 region : false, /// set by caller
40759 disableTooltips : true, // not used yet...
40762 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40763 * @param {String} id The id of the div to use <b>or create</b>
40764 * @param {String} text The text for the tab
40765 * @param {String} content (optional) Content to put in the TabPanelItem body
40766 * @param {Boolean} closable (optional) True to create a close icon on the tab
40767 * @return {Roo.TabPanelItem} The created TabPanelItem
40769 addTab : function(id, text, content, closable, tpl)
40771 var item = new Roo.bootstrap.panel.TabItem({
40775 closable : closable,
40778 this.addTabItem(item);
40780 item.setContent(content);
40786 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40787 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40788 * @return {Roo.TabPanelItem}
40790 getTab : function(id){
40791 return this.items[id];
40795 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40796 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40798 hideTab : function(id){
40799 var t = this.items[id];
40802 this.hiddenCount++;
40803 this.autoSizeTabs();
40808 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40809 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40811 unhideTab : function(id){
40812 var t = this.items[id];
40814 t.setHidden(false);
40815 this.hiddenCount--;
40816 this.autoSizeTabs();
40821 * Adds an existing {@link Roo.TabPanelItem}.
40822 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40824 addTabItem : function(item)
40826 this.items[item.id] = item;
40827 this.items.push(item);
40828 this.autoSizeTabs();
40829 // if(this.resizeTabs){
40830 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40831 // this.autoSizeTabs();
40833 // item.autoSize();
40838 * Removes a {@link Roo.TabPanelItem}.
40839 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40841 removeTab : function(id){
40842 var items = this.items;
40843 var tab = items[id];
40844 if(!tab) { return; }
40845 var index = items.indexOf(tab);
40846 if(this.active == tab && items.length > 1){
40847 var newTab = this.getNextAvailable(index);
40852 this.stripEl.dom.removeChild(tab.pnode.dom);
40853 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40854 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40856 items.splice(index, 1);
40857 delete this.items[tab.id];
40858 tab.fireEvent("close", tab);
40859 tab.purgeListeners();
40860 this.autoSizeTabs();
40863 getNextAvailable : function(start){
40864 var items = this.items;
40866 // look for a next tab that will slide over to
40867 // replace the one being removed
40868 while(index < items.length){
40869 var item = items[++index];
40870 if(item && !item.isHidden()){
40874 // if one isn't found select the previous tab (on the left)
40877 var item = items[--index];
40878 if(item && !item.isHidden()){
40886 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40887 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40889 disableTab : function(id){
40890 var tab = this.items[id];
40891 if(tab && this.active != tab){
40897 * Enables a {@link Roo.TabPanelItem} that is disabled.
40898 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40900 enableTab : function(id){
40901 var tab = this.items[id];
40906 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40907 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40908 * @return {Roo.TabPanelItem} The TabPanelItem.
40910 activate : function(id)
40912 //Roo.log('activite:' + id);
40914 var tab = this.items[id];
40918 if(tab == this.active || tab.disabled){
40922 this.fireEvent("beforetabchange", this, e, tab);
40923 if(e.cancel !== true && !tab.disabled){
40925 this.active.hide();
40927 this.active = this.items[id];
40928 this.active.show();
40929 this.fireEvent("tabchange", this, this.active);
40935 * Gets the active {@link Roo.TabPanelItem}.
40936 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40938 getActiveTab : function(){
40939 return this.active;
40943 * Updates the tab body element to fit the height of the container element
40944 * for overflow scrolling
40945 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40947 syncHeight : function(targetHeight){
40948 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40949 var bm = this.bodyEl.getMargins();
40950 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40951 this.bodyEl.setHeight(newHeight);
40955 onResize : function(){
40956 if(this.monitorResize){
40957 this.autoSizeTabs();
40962 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40964 beginUpdate : function(){
40965 this.updating = true;
40969 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40971 endUpdate : function(){
40972 this.updating = false;
40973 this.autoSizeTabs();
40977 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40979 autoSizeTabs : function()
40981 var count = this.items.length;
40982 var vcount = count - this.hiddenCount;
40985 this.stripEl.hide();
40987 this.stripEl.show();
40990 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40995 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40996 var availWidth = Math.floor(w / vcount);
40997 var b = this.stripBody;
40998 if(b.getWidth() > w){
40999 var tabs = this.items;
41000 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41001 if(availWidth < this.minTabWidth){
41002 /*if(!this.sleft){ // incomplete scrolling code
41003 this.createScrollButtons();
41006 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41009 if(this.currentTabWidth < this.preferredTabWidth){
41010 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41016 * Returns the number of tabs in this TabPanel.
41019 getCount : function(){
41020 return this.items.length;
41024 * Resizes all the tabs to the passed width
41025 * @param {Number} The new width
41027 setTabWidth : function(width){
41028 this.currentTabWidth = width;
41029 for(var i = 0, len = this.items.length; i < len; i++) {
41030 if(!this.items[i].isHidden()) {
41031 this.items[i].setWidth(width);
41037 * Destroys this TabPanel
41038 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41040 destroy : function(removeEl){
41041 Roo.EventManager.removeResizeListener(this.onResize, this);
41042 for(var i = 0, len = this.items.length; i < len; i++){
41043 this.items[i].purgeListeners();
41045 if(removeEl === true){
41046 this.el.update("");
41051 createStrip : function(container)
41053 var strip = document.createElement("nav");
41054 strip.className = Roo.bootstrap.version == 4 ?
41055 "navbar-light bg-light" :
41056 "navbar navbar-default"; //"x-tabs-wrap";
41057 container.appendChild(strip);
41061 createStripList : function(strip)
41063 // div wrapper for retard IE
41064 // returns the "tr" element.
41065 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41066 //'<div class="x-tabs-strip-wrap">'+
41067 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41068 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41069 return strip.firstChild; //.firstChild.firstChild.firstChild;
41071 createBody : function(container)
41073 var body = document.createElement("div");
41074 Roo.id(body, "tab-body");
41075 //Roo.fly(body).addClass("x-tabs-body");
41076 Roo.fly(body).addClass("tab-content");
41077 container.appendChild(body);
41080 createItemBody :function(bodyEl, id){
41081 var body = Roo.getDom(id);
41083 body = document.createElement("div");
41086 //Roo.fly(body).addClass("x-tabs-item-body");
41087 Roo.fly(body).addClass("tab-pane");
41088 bodyEl.insertBefore(body, bodyEl.firstChild);
41092 createStripElements : function(stripEl, text, closable, tpl)
41094 var td = document.createElement("li"); // was td..
41095 td.className = 'nav-item';
41097 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41100 stripEl.appendChild(td);
41102 td.className = "x-tabs-closable";
41103 if(!this.closeTpl){
41104 this.closeTpl = new Roo.Template(
41105 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41106 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41107 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41110 var el = this.closeTpl.overwrite(td, {"text": text});
41111 var close = el.getElementsByTagName("div")[0];
41112 var inner = el.getElementsByTagName("em")[0];
41113 return {"el": el, "close": close, "inner": inner};
41116 // not sure what this is..
41117 // if(!this.tabTpl){
41118 //this.tabTpl = new Roo.Template(
41119 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41120 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41122 // this.tabTpl = new Roo.Template(
41123 // '<a href="#">' +
41124 // '<span unselectable="on"' +
41125 // (this.disableTooltips ? '' : ' title="{text}"') +
41126 // ' >{text}</span></a>'
41132 var template = tpl || this.tabTpl || false;
41135 template = new Roo.Template(
41136 Roo.bootstrap.version == 4 ?
41138 '<a class="nav-link" href="#" unselectable="on"' +
41139 (this.disableTooltips ? '' : ' title="{text}"') +
41142 '<a class="nav-link" href="#">' +
41143 '<span unselectable="on"' +
41144 (this.disableTooltips ? '' : ' title="{text}"') +
41145 ' >{text}</span></a>'
41150 switch (typeof(template)) {
41154 template = new Roo.Template(template);
41160 var el = template.overwrite(td, {"text": text});
41162 var inner = el.getElementsByTagName("span")[0];
41164 return {"el": el, "inner": inner};
41172 * @class Roo.TabPanelItem
41173 * @extends Roo.util.Observable
41174 * Represents an individual item (tab plus body) in a TabPanel.
41175 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41176 * @param {String} id The id of this TabPanelItem
41177 * @param {String} text The text for the tab of this TabPanelItem
41178 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41180 Roo.bootstrap.panel.TabItem = function(config){
41182 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41183 * @type Roo.TabPanel
41185 this.tabPanel = config.panel;
41187 * The id for this TabPanelItem
41190 this.id = config.id;
41192 this.disabled = false;
41194 this.text = config.text;
41196 this.loaded = false;
41197 this.closable = config.closable;
41200 * The body element for this TabPanelItem.
41201 * @type Roo.Element
41203 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41204 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41205 this.bodyEl.setStyle("display", "block");
41206 this.bodyEl.setStyle("zoom", "1");
41207 //this.hideAction();
41209 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41211 this.el = Roo.get(els.el);
41212 this.inner = Roo.get(els.inner, true);
41213 this.textEl = Roo.bootstrap.version == 4 ?
41214 this.el : Roo.get(this.el.dom.firstChild, true);
41216 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41217 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41220 // this.el.on("mousedown", this.onTabMouseDown, this);
41221 this.el.on("click", this.onTabClick, this);
41223 if(config.closable){
41224 var c = Roo.get(els.close, true);
41225 c.dom.title = this.closeText;
41226 c.addClassOnOver("close-over");
41227 c.on("click", this.closeClick, this);
41233 * Fires when this tab becomes the active tab.
41234 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41235 * @param {Roo.TabPanelItem} this
41239 * @event beforeclose
41240 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41241 * @param {Roo.TabPanelItem} this
41242 * @param {Object} e Set cancel to true on this object to cancel the close.
41244 "beforeclose": true,
41247 * Fires when this tab is closed.
41248 * @param {Roo.TabPanelItem} this
41252 * @event deactivate
41253 * Fires when this tab is no longer the active tab.
41254 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41255 * @param {Roo.TabPanelItem} this
41257 "deactivate" : true
41259 this.hidden = false;
41261 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41264 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41266 purgeListeners : function(){
41267 Roo.util.Observable.prototype.purgeListeners.call(this);
41268 this.el.removeAllListeners();
41271 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41274 this.status_node.addClass("active");
41277 this.tabPanel.stripWrap.repaint();
41279 this.fireEvent("activate", this.tabPanel, this);
41283 * Returns true if this tab is the active tab.
41284 * @return {Boolean}
41286 isActive : function(){
41287 return this.tabPanel.getActiveTab() == this;
41291 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41294 this.status_node.removeClass("active");
41296 this.fireEvent("deactivate", this.tabPanel, this);
41299 hideAction : function(){
41300 this.bodyEl.hide();
41301 this.bodyEl.setStyle("position", "absolute");
41302 this.bodyEl.setLeft("-20000px");
41303 this.bodyEl.setTop("-20000px");
41306 showAction : function(){
41307 this.bodyEl.setStyle("position", "relative");
41308 this.bodyEl.setTop("");
41309 this.bodyEl.setLeft("");
41310 this.bodyEl.show();
41314 * Set the tooltip for the tab.
41315 * @param {String} tooltip The tab's tooltip
41317 setTooltip : function(text){
41318 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41319 this.textEl.dom.qtip = text;
41320 this.textEl.dom.removeAttribute('title');
41322 this.textEl.dom.title = text;
41326 onTabClick : function(e){
41327 e.preventDefault();
41328 this.tabPanel.activate(this.id);
41331 onTabMouseDown : function(e){
41332 e.preventDefault();
41333 this.tabPanel.activate(this.id);
41336 getWidth : function(){
41337 return this.inner.getWidth();
41340 setWidth : function(width){
41341 var iwidth = width - this.linode.getPadding("lr");
41342 this.inner.setWidth(iwidth);
41343 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41344 this.linode.setWidth(width);
41348 * Show or hide the tab
41349 * @param {Boolean} hidden True to hide or false to show.
41351 setHidden : function(hidden){
41352 this.hidden = hidden;
41353 this.linode.setStyle("display", hidden ? "none" : "");
41357 * Returns true if this tab is "hidden"
41358 * @return {Boolean}
41360 isHidden : function(){
41361 return this.hidden;
41365 * Returns the text for this tab
41368 getText : function(){
41372 autoSize : function(){
41373 //this.el.beginMeasure();
41374 this.textEl.setWidth(1);
41376 * #2804 [new] Tabs in Roojs
41377 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41379 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41380 //this.el.endMeasure();
41384 * Sets the text for the tab (Note: this also sets the tooltip text)
41385 * @param {String} text The tab's text and tooltip
41387 setText : function(text){
41389 this.textEl.update(text);
41390 this.setTooltip(text);
41391 //if(!this.tabPanel.resizeTabs){
41392 // this.autoSize();
41396 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41398 activate : function(){
41399 this.tabPanel.activate(this.id);
41403 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41405 disable : function(){
41406 if(this.tabPanel.active != this){
41407 this.disabled = true;
41408 this.status_node.addClass("disabled");
41413 * Enables this TabPanelItem if it was previously disabled.
41415 enable : function(){
41416 this.disabled = false;
41417 this.status_node.removeClass("disabled");
41421 * Sets the content for this TabPanelItem.
41422 * @param {String} content The content
41423 * @param {Boolean} loadScripts true to look for and load scripts
41425 setContent : function(content, loadScripts){
41426 this.bodyEl.update(content, loadScripts);
41430 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41431 * @return {Roo.UpdateManager} The UpdateManager
41433 getUpdateManager : function(){
41434 return this.bodyEl.getUpdateManager();
41438 * Set a URL to be used to load the content for this TabPanelItem.
41439 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41440 * @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)
41441 * @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)
41442 * @return {Roo.UpdateManager} The UpdateManager
41444 setUrl : function(url, params, loadOnce){
41445 if(this.refreshDelegate){
41446 this.un('activate', this.refreshDelegate);
41448 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41449 this.on("activate", this.refreshDelegate);
41450 return this.bodyEl.getUpdateManager();
41454 _handleRefresh : function(url, params, loadOnce){
41455 if(!loadOnce || !this.loaded){
41456 var updater = this.bodyEl.getUpdateManager();
41457 updater.update(url, params, this._setLoaded.createDelegate(this));
41462 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41463 * Will fail silently if the setUrl method has not been called.
41464 * This does not activate the panel, just updates its content.
41466 refresh : function(){
41467 if(this.refreshDelegate){
41468 this.loaded = false;
41469 this.refreshDelegate();
41474 _setLoaded : function(){
41475 this.loaded = true;
41479 closeClick : function(e){
41482 this.fireEvent("beforeclose", this, o);
41483 if(o.cancel !== true){
41484 this.tabPanel.removeTab(this.id);
41488 * The text displayed in the tooltip for the close icon.
41491 closeText : "Close this tab"
41494 * This script refer to:
41495 * Title: International Telephone Input
41496 * Author: Jack O'Connor
41497 * Code version: v12.1.12
41498 * Availability: https://github.com/jackocnr/intl-tel-input.git
41501 Roo.bootstrap.PhoneInputData = function() {
41504 "Afghanistan (افغانستان)",
41509 "Albania (Shqipëri)",
41514 "Algeria (الجزائر)",
41539 "Antigua and Barbuda",
41549 "Armenia (Հայաստան)",
41565 "Austria (Österreich)",
41570 "Azerbaijan (Azərbaycan)",
41580 "Bahrain (البحرين)",
41585 "Bangladesh (বাংলাদেশ)",
41595 "Belarus (Беларусь)",
41600 "Belgium (België)",
41630 "Bosnia and Herzegovina (Босна и Херцеговина)",
41645 "British Indian Ocean Territory",
41650 "British Virgin Islands",
41660 "Bulgaria (България)",
41670 "Burundi (Uburundi)",
41675 "Cambodia (កម្ពុជា)",
41680 "Cameroon (Cameroun)",
41689 ["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"]
41692 "Cape Verde (Kabu Verdi)",
41697 "Caribbean Netherlands",
41708 "Central African Republic (République centrafricaine)",
41728 "Christmas Island",
41734 "Cocos (Keeling) Islands",
41745 "Comoros (جزر القمر)",
41750 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41755 "Congo (Republic) (Congo-Brazzaville)",
41775 "Croatia (Hrvatska)",
41796 "Czech Republic (Česká republika)",
41801 "Denmark (Danmark)",
41816 "Dominican Republic (República Dominicana)",
41820 ["809", "829", "849"]
41838 "Equatorial Guinea (Guinea Ecuatorial)",
41858 "Falkland Islands (Islas Malvinas)",
41863 "Faroe Islands (Føroyar)",
41884 "French Guiana (Guyane française)",
41889 "French Polynesia (Polynésie française)",
41904 "Georgia (საქართველო)",
41909 "Germany (Deutschland)",
41929 "Greenland (Kalaallit Nunaat)",
41966 "Guinea-Bissau (Guiné Bissau)",
41991 "Hungary (Magyarország)",
41996 "Iceland (Ísland)",
42016 "Iraq (العراق)",
42032 "Israel (ישראל)",
42059 "Jordan (الأردن)",
42064 "Kazakhstan (Казахстан)",
42085 "Kuwait (الكويت)",
42090 "Kyrgyzstan (Кыргызстан)",
42100 "Latvia (Latvija)",
42105 "Lebanon (لبنان)",
42120 "Libya (ليبيا)",
42130 "Lithuania (Lietuva)",
42145 "Macedonia (FYROM) (Македонија)",
42150 "Madagascar (Madagasikara)",
42180 "Marshall Islands",
42190 "Mauritania (موريتانيا)",
42195 "Mauritius (Moris)",
42216 "Moldova (Republica Moldova)",
42226 "Mongolia (Монгол)",
42231 "Montenegro (Crna Gora)",
42241 "Morocco (المغرب)",
42247 "Mozambique (Moçambique)",
42252 "Myanmar (Burma) (မြန်မာ)",
42257 "Namibia (Namibië)",
42272 "Netherlands (Nederland)",
42277 "New Caledonia (Nouvelle-Calédonie)",
42312 "North Korea (조선 민주주의 인민 공화국)",
42317 "Northern Mariana Islands",
42333 "Pakistan (پاکستان)",
42343 "Palestine (فلسطين)",
42353 "Papua New Guinea",
42395 "Réunion (La Réunion)",
42401 "Romania (România)",
42417 "Saint Barthélemy",
42428 "Saint Kitts and Nevis",
42438 "Saint Martin (Saint-Martin (partie française))",
42444 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42449 "Saint Vincent and the Grenadines",
42464 "São Tomé and Príncipe (São Tomé e Príncipe)",
42469 "Saudi Arabia (المملكة العربية السعودية)",
42474 "Senegal (Sénégal)",
42504 "Slovakia (Slovensko)",
42509 "Slovenia (Slovenija)",
42519 "Somalia (Soomaaliya)",
42529 "South Korea (대한민국)",
42534 "South Sudan (جنوب السودان)",
42544 "Sri Lanka (ශ්රී ලංකාව)",
42549 "Sudan (السودان)",
42559 "Svalbard and Jan Mayen",
42570 "Sweden (Sverige)",
42575 "Switzerland (Schweiz)",
42580 "Syria (سوريا)",
42625 "Trinidad and Tobago",
42630 "Tunisia (تونس)",
42635 "Turkey (Türkiye)",
42645 "Turks and Caicos Islands",
42655 "U.S. Virgin Islands",
42665 "Ukraine (Україна)",
42670 "United Arab Emirates (الإمارات العربية المتحدة)",
42692 "Uzbekistan (Oʻzbekiston)",
42702 "Vatican City (Città del Vaticano)",
42713 "Vietnam (Việt Nam)",
42718 "Wallis and Futuna (Wallis-et-Futuna)",
42723 "Western Sahara (الصحراء الغربية)",
42729 "Yemen (اليمن)",
42753 * This script refer to:
42754 * Title: International Telephone Input
42755 * Author: Jack O'Connor
42756 * Code version: v12.1.12
42757 * Availability: https://github.com/jackocnr/intl-tel-input.git
42761 * @class Roo.bootstrap.PhoneInput
42762 * @extends Roo.bootstrap.TriggerField
42763 * An input with International dial-code selection
42765 * @cfg {String} defaultDialCode default '+852'
42766 * @cfg {Array} preferedCountries default []
42769 * Create a new PhoneInput.
42770 * @param {Object} config Configuration options
42773 Roo.bootstrap.PhoneInput = function(config) {
42774 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42777 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42779 listWidth: undefined,
42781 selectedClass: 'active',
42783 invalidClass : "has-warning",
42785 validClass: 'has-success',
42787 allowed: '0123456789',
42792 * @cfg {String} defaultDialCode The default dial code when initializing the input
42794 defaultDialCode: '+852',
42797 * @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
42799 preferedCountries: false,
42801 getAutoCreate : function()
42803 var data = Roo.bootstrap.PhoneInputData();
42804 var align = this.labelAlign || this.parentLabelAlign();
42807 this.allCountries = [];
42808 this.dialCodeMapping = [];
42810 for (var i = 0; i < data.length; i++) {
42812 this.allCountries[i] = {
42816 priority: c[3] || 0,
42817 areaCodes: c[4] || null
42819 this.dialCodeMapping[c[2]] = {
42822 priority: c[3] || 0,
42823 areaCodes: c[4] || null
42835 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42836 maxlength: this.max_length,
42837 cls : 'form-control tel-input',
42838 autocomplete: 'new-password'
42841 var hiddenInput = {
42844 cls: 'hidden-tel-input'
42848 hiddenInput.name = this.name;
42851 if (this.disabled) {
42852 input.disabled = true;
42855 var flag_container = {
42872 cls: this.hasFeedback ? 'has-feedback' : '',
42878 cls: 'dial-code-holder',
42885 cls: 'roo-select2-container input-group',
42892 if (this.fieldLabel.length) {
42895 tooltip: 'This field is required'
42901 cls: 'control-label',
42907 html: this.fieldLabel
42910 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42916 if(this.indicatorpos == 'right') {
42917 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42924 if(align == 'left') {
42932 if(this.labelWidth > 12){
42933 label.style = "width: " + this.labelWidth + 'px';
42935 if(this.labelWidth < 13 && this.labelmd == 0){
42936 this.labelmd = this.labelWidth;
42938 if(this.labellg > 0){
42939 label.cls += ' col-lg-' + this.labellg;
42940 input.cls += ' col-lg-' + (12 - this.labellg);
42942 if(this.labelmd > 0){
42943 label.cls += ' col-md-' + this.labelmd;
42944 container.cls += ' col-md-' + (12 - this.labelmd);
42946 if(this.labelsm > 0){
42947 label.cls += ' col-sm-' + this.labelsm;
42948 container.cls += ' col-sm-' + (12 - this.labelsm);
42950 if(this.labelxs > 0){
42951 label.cls += ' col-xs-' + this.labelxs;
42952 container.cls += ' col-xs-' + (12 - this.labelxs);
42962 var settings = this;
42964 ['xs','sm','md','lg'].map(function(size){
42965 if (settings[size]) {
42966 cfg.cls += ' col-' + size + '-' + settings[size];
42970 this.store = new Roo.data.Store({
42971 proxy : new Roo.data.MemoryProxy({}),
42972 reader : new Roo.data.JsonReader({
42983 'name' : 'dialCode',
42987 'name' : 'priority',
42991 'name' : 'areaCodes',
42998 if(!this.preferedCountries) {
42999 this.preferedCountries = [
43006 var p = this.preferedCountries.reverse();
43009 for (var i = 0; i < p.length; i++) {
43010 for (var j = 0; j < this.allCountries.length; j++) {
43011 if(this.allCountries[j].iso2 == p[i]) {
43012 var t = this.allCountries[j];
43013 this.allCountries.splice(j,1);
43014 this.allCountries.unshift(t);
43020 this.store.proxy.data = {
43022 data: this.allCountries
43028 initEvents : function()
43031 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43033 this.indicator = this.indicatorEl();
43034 this.flag = this.flagEl();
43035 this.dialCodeHolder = this.dialCodeHolderEl();
43037 this.trigger = this.el.select('div.flag-box',true).first();
43038 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43043 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43044 _this.list.setWidth(lw);
43047 this.list.on('mouseover', this.onViewOver, this);
43048 this.list.on('mousemove', this.onViewMove, this);
43049 this.inputEl().on("keyup", this.onKeyUp, this);
43050 this.inputEl().on("keypress", this.onKeyPress, this);
43052 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43054 this.view = new Roo.View(this.list, this.tpl, {
43055 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43058 this.view.on('click', this.onViewClick, this);
43059 this.setValue(this.defaultDialCode);
43062 onTriggerClick : function(e)
43064 Roo.log('trigger click');
43069 if(this.isExpanded()){
43071 this.hasFocus = false;
43073 this.store.load({});
43074 this.hasFocus = true;
43079 isExpanded : function()
43081 return this.list.isVisible();
43084 collapse : function()
43086 if(!this.isExpanded()){
43090 Roo.get(document).un('mousedown', this.collapseIf, this);
43091 Roo.get(document).un('mousewheel', this.collapseIf, this);
43092 this.fireEvent('collapse', this);
43096 expand : function()
43100 if(this.isExpanded() || !this.hasFocus){
43104 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43105 this.list.setWidth(lw);
43108 this.restrictHeight();
43110 Roo.get(document).on('mousedown', this.collapseIf, this);
43111 Roo.get(document).on('mousewheel', this.collapseIf, this);
43113 this.fireEvent('expand', this);
43116 restrictHeight : function()
43118 this.list.alignTo(this.inputEl(), this.listAlign);
43119 this.list.alignTo(this.inputEl(), this.listAlign);
43122 onViewOver : function(e, t)
43124 if(this.inKeyMode){
43127 var item = this.view.findItemFromChild(t);
43130 var index = this.view.indexOf(item);
43131 this.select(index, false);
43136 onViewClick : function(view, doFocus, el, e)
43138 var index = this.view.getSelectedIndexes()[0];
43140 var r = this.store.getAt(index);
43143 this.onSelect(r, index);
43145 if(doFocus !== false && !this.blockFocus){
43146 this.inputEl().focus();
43150 onViewMove : function(e, t)
43152 this.inKeyMode = false;
43155 select : function(index, scrollIntoView)
43157 this.selectedIndex = index;
43158 this.view.select(index);
43159 if(scrollIntoView !== false){
43160 var el = this.view.getNode(index);
43162 this.list.scrollChildIntoView(el, false);
43167 createList : function()
43169 this.list = Roo.get(document.body).createChild({
43171 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43172 style: 'display:none'
43175 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43178 collapseIf : function(e)
43180 var in_combo = e.within(this.el);
43181 var in_list = e.within(this.list);
43182 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43184 if (in_combo || in_list || is_list) {
43190 onSelect : function(record, index)
43192 if(this.fireEvent('beforeselect', this, record, index) !== false){
43194 this.setFlagClass(record.data.iso2);
43195 this.setDialCode(record.data.dialCode);
43196 this.hasFocus = false;
43198 this.fireEvent('select', this, record, index);
43202 flagEl : function()
43204 var flag = this.el.select('div.flag',true).first();
43211 dialCodeHolderEl : function()
43213 var d = this.el.select('input.dial-code-holder',true).first();
43220 setDialCode : function(v)
43222 this.dialCodeHolder.dom.value = '+'+v;
43225 setFlagClass : function(n)
43227 this.flag.dom.className = 'flag '+n;
43230 getValue : function()
43232 var v = this.inputEl().getValue();
43233 if(this.dialCodeHolder) {
43234 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43239 setValue : function(v)
43241 var d = this.getDialCode(v);
43243 //invalid dial code
43244 if(v.length == 0 || !d || d.length == 0) {
43246 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43247 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43253 this.setFlagClass(this.dialCodeMapping[d].iso2);
43254 this.setDialCode(d);
43255 this.inputEl().dom.value = v.replace('+'+d,'');
43256 this.hiddenEl().dom.value = this.getValue();
43261 getDialCode : function(v)
43265 if (v.length == 0) {
43266 return this.dialCodeHolder.dom.value;
43270 if (v.charAt(0) != "+") {
43273 var numericChars = "";
43274 for (var i = 1; i < v.length; i++) {
43275 var c = v.charAt(i);
43278 if (this.dialCodeMapping[numericChars]) {
43279 dialCode = v.substr(1, i);
43281 if (numericChars.length == 4) {
43291 this.setValue(this.defaultDialCode);
43295 hiddenEl : function()
43297 return this.el.select('input.hidden-tel-input',true).first();
43300 // after setting val
43301 onKeyUp : function(e){
43302 this.setValue(this.getValue());
43305 onKeyPress : function(e){
43306 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43313 * @class Roo.bootstrap.MoneyField
43314 * @extends Roo.bootstrap.ComboBox
43315 * Bootstrap MoneyField class
43318 * Create a new MoneyField.
43319 * @param {Object} config Configuration options
43322 Roo.bootstrap.MoneyField = function(config) {
43324 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43328 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43331 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43333 allowDecimals : true,
43335 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43337 decimalSeparator : ".",
43339 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43341 decimalPrecision : 0,
43343 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43345 allowNegative : true,
43347 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43351 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43353 minValue : Number.NEGATIVE_INFINITY,
43355 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43357 maxValue : Number.MAX_VALUE,
43359 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43361 minText : "The minimum value for this field is {0}",
43363 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43365 maxText : "The maximum value for this field is {0}",
43367 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43368 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43370 nanText : "{0} is not a valid number",
43372 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43376 * @cfg {String} defaults currency of the MoneyField
43377 * value should be in lkey
43379 defaultCurrency : false,
43381 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43383 thousandsDelimiter : false,
43385 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43396 getAutoCreate : function()
43398 var align = this.labelAlign || this.parentLabelAlign();
43410 cls : 'form-control roo-money-amount-input',
43411 autocomplete: 'new-password'
43414 var hiddenInput = {
43418 cls: 'hidden-number-input'
43421 if(this.max_length) {
43422 input.maxlength = this.max_length;
43426 hiddenInput.name = this.name;
43429 if (this.disabled) {
43430 input.disabled = true;
43433 var clg = 12 - this.inputlg;
43434 var cmd = 12 - this.inputmd;
43435 var csm = 12 - this.inputsm;
43436 var cxs = 12 - this.inputxs;
43440 cls : 'row roo-money-field',
43444 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43448 cls: 'roo-select2-container input-group',
43452 cls : 'form-control roo-money-currency-input',
43453 autocomplete: 'new-password',
43455 name : this.currencyName
43459 cls : 'input-group-addon',
43473 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43477 cls: this.hasFeedback ? 'has-feedback' : '',
43488 if (this.fieldLabel.length) {
43491 tooltip: 'This field is required'
43497 cls: 'control-label',
43503 html: this.fieldLabel
43506 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43512 if(this.indicatorpos == 'right') {
43513 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43520 if(align == 'left') {
43528 if(this.labelWidth > 12){
43529 label.style = "width: " + this.labelWidth + 'px';
43531 if(this.labelWidth < 13 && this.labelmd == 0){
43532 this.labelmd = this.labelWidth;
43534 if(this.labellg > 0){
43535 label.cls += ' col-lg-' + this.labellg;
43536 input.cls += ' col-lg-' + (12 - this.labellg);
43538 if(this.labelmd > 0){
43539 label.cls += ' col-md-' + this.labelmd;
43540 container.cls += ' col-md-' + (12 - this.labelmd);
43542 if(this.labelsm > 0){
43543 label.cls += ' col-sm-' + this.labelsm;
43544 container.cls += ' col-sm-' + (12 - this.labelsm);
43546 if(this.labelxs > 0){
43547 label.cls += ' col-xs-' + this.labelxs;
43548 container.cls += ' col-xs-' + (12 - this.labelxs);
43559 var settings = this;
43561 ['xs','sm','md','lg'].map(function(size){
43562 if (settings[size]) {
43563 cfg.cls += ' col-' + size + '-' + settings[size];
43570 initEvents : function()
43572 this.indicator = this.indicatorEl();
43574 this.initCurrencyEvent();
43576 this.initNumberEvent();
43579 initCurrencyEvent : function()
43582 throw "can not find store for combo";
43585 this.store = Roo.factory(this.store, Roo.data);
43586 this.store.parent = this;
43590 this.triggerEl = this.el.select('.input-group-addon', true).first();
43592 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43597 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43598 _this.list.setWidth(lw);
43601 this.list.on('mouseover', this.onViewOver, this);
43602 this.list.on('mousemove', this.onViewMove, this);
43603 this.list.on('scroll', this.onViewScroll, this);
43606 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43609 this.view = new Roo.View(this.list, this.tpl, {
43610 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43613 this.view.on('click', this.onViewClick, this);
43615 this.store.on('beforeload', this.onBeforeLoad, this);
43616 this.store.on('load', this.onLoad, this);
43617 this.store.on('loadexception', this.onLoadException, this);
43619 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43620 "up" : function(e){
43621 this.inKeyMode = true;
43625 "down" : function(e){
43626 if(!this.isExpanded()){
43627 this.onTriggerClick();
43629 this.inKeyMode = true;
43634 "enter" : function(e){
43637 if(this.fireEvent("specialkey", this, e)){
43638 this.onViewClick(false);
43644 "esc" : function(e){
43648 "tab" : function(e){
43651 if(this.fireEvent("specialkey", this, e)){
43652 this.onViewClick(false);
43660 doRelay : function(foo, bar, hname){
43661 if(hname == 'down' || this.scope.isExpanded()){
43662 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43670 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43674 initNumberEvent : function(e)
43676 this.inputEl().on("keydown" , this.fireKey, this);
43677 this.inputEl().on("focus", this.onFocus, this);
43678 this.inputEl().on("blur", this.onBlur, this);
43680 this.inputEl().relayEvent('keyup', this);
43682 if(this.indicator){
43683 this.indicator.addClass('invisible');
43686 this.originalValue = this.getValue();
43688 if(this.validationEvent == 'keyup'){
43689 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43690 this.inputEl().on('keyup', this.filterValidation, this);
43692 else if(this.validationEvent !== false){
43693 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43696 if(this.selectOnFocus){
43697 this.on("focus", this.preFocus, this);
43700 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43701 this.inputEl().on("keypress", this.filterKeys, this);
43703 this.inputEl().relayEvent('keypress', this);
43706 var allowed = "0123456789";
43708 if(this.allowDecimals){
43709 allowed += this.decimalSeparator;
43712 if(this.allowNegative){
43716 if(this.thousandsDelimiter) {
43720 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43722 var keyPress = function(e){
43724 var k = e.getKey();
43726 var c = e.getCharCode();
43729 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43730 allowed.indexOf(String.fromCharCode(c)) === -1
43736 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43740 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43745 this.inputEl().on("keypress", keyPress, this);
43749 onTriggerClick : function(e)
43756 this.loadNext = false;
43758 if(this.isExpanded()){
43763 this.hasFocus = true;
43765 if(this.triggerAction == 'all') {
43766 this.doQuery(this.allQuery, true);
43770 this.doQuery(this.getRawValue());
43773 getCurrency : function()
43775 var v = this.currencyEl().getValue();
43780 restrictHeight : function()
43782 this.list.alignTo(this.currencyEl(), this.listAlign);
43783 this.list.alignTo(this.currencyEl(), this.listAlign);
43786 onViewClick : function(view, doFocus, el, e)
43788 var index = this.view.getSelectedIndexes()[0];
43790 var r = this.store.getAt(index);
43793 this.onSelect(r, index);
43797 onSelect : function(record, index){
43799 if(this.fireEvent('beforeselect', this, record, index) !== false){
43801 this.setFromCurrencyData(index > -1 ? record.data : false);
43805 this.fireEvent('select', this, record, index);
43809 setFromCurrencyData : function(o)
43813 this.lastCurrency = o;
43815 if (this.currencyField) {
43816 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43818 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43821 this.lastSelectionText = currency;
43823 //setting default currency
43824 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43825 this.setCurrency(this.defaultCurrency);
43829 this.setCurrency(currency);
43832 setFromData : function(o)
43836 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43838 this.setFromCurrencyData(c);
43843 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43845 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43848 this.setValue(value);
43852 setCurrency : function(v)
43854 this.currencyValue = v;
43857 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43862 setValue : function(v)
43864 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43870 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43872 this.inputEl().dom.value = (v == '') ? '' :
43873 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43875 if(!this.allowZero && v === '0') {
43876 this.hiddenEl().dom.value = '';
43877 this.inputEl().dom.value = '';
43884 getRawValue : function()
43886 var v = this.inputEl().getValue();
43891 getValue : function()
43893 return this.fixPrecision(this.parseValue(this.getRawValue()));
43896 parseValue : function(value)
43898 if(this.thousandsDelimiter) {
43900 r = new RegExp(",", "g");
43901 value = value.replace(r, "");
43904 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43905 return isNaN(value) ? '' : value;
43909 fixPrecision : function(value)
43911 if(this.thousandsDelimiter) {
43913 r = new RegExp(",", "g");
43914 value = value.replace(r, "");
43917 var nan = isNaN(value);
43919 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43920 return nan ? '' : value;
43922 return parseFloat(value).toFixed(this.decimalPrecision);
43925 decimalPrecisionFcn : function(v)
43927 return Math.floor(v);
43930 validateValue : function(value)
43932 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43936 var num = this.parseValue(value);
43939 this.markInvalid(String.format(this.nanText, value));
43943 if(num < this.minValue){
43944 this.markInvalid(String.format(this.minText, this.minValue));
43948 if(num > this.maxValue){
43949 this.markInvalid(String.format(this.maxText, this.maxValue));
43956 validate : function()
43958 if(this.disabled || this.allowBlank){
43963 var currency = this.getCurrency();
43965 if(this.validateValue(this.getRawValue()) && currency.length){
43970 this.markInvalid();
43974 getName: function()
43979 beforeBlur : function()
43985 var v = this.parseValue(this.getRawValue());
43992 onBlur : function()
43996 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43997 //this.el.removeClass(this.focusClass);
44000 this.hasFocus = false;
44002 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44006 var v = this.getValue();
44008 if(String(v) !== String(this.startValue)){
44009 this.fireEvent('change', this, v, this.startValue);
44012 this.fireEvent("blur", this);
44015 inputEl : function()
44017 return this.el.select('.roo-money-amount-input', true).first();
44020 currencyEl : function()
44022 return this.el.select('.roo-money-currency-input', true).first();
44025 hiddenEl : function()
44027 return this.el.select('input.hidden-number-input',true).first();
44031 * @class Roo.bootstrap.BezierSignature
44032 * @extends Roo.bootstrap.Component
44033 * Bootstrap BezierSignature class
44034 * This script refer to:
44035 * Title: Signature Pad
44037 * Availability: https://github.com/szimek/signature_pad
44040 * Create a new BezierSignature
44041 * @param {Object} config The config object
44044 Roo.bootstrap.BezierSignature = function(config){
44045 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44051 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44058 mouse_btn_down: true,
44061 * @cfg {int} canvas height
44063 canvas_height: '200px',
44066 * @cfg {float|function} Radius of a single dot.
44071 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44076 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44081 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44086 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44091 * @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.
44093 bg_color: 'rgba(0, 0, 0, 0)',
44096 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44098 dot_color: 'black',
44101 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44103 velocity_filter_weight: 0.7,
44106 * @cfg {function} Callback when stroke begin.
44111 * @cfg {function} Callback when stroke end.
44115 getAutoCreate : function()
44117 var cls = 'roo-signature column';
44120 cls += ' ' + this.cls;
44130 for(var i = 0; i < col_sizes.length; i++) {
44131 if(this[col_sizes[i]]) {
44132 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44142 cls: 'roo-signature-body',
44146 cls: 'roo-signature-body-canvas',
44147 height: this.canvas_height,
44148 width: this.canvas_width
44155 style: 'display: none'
44163 initEvents: function()
44165 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44167 var canvas = this.canvasEl();
44169 // mouse && touch event swapping...
44170 canvas.dom.style.touchAction = 'none';
44171 canvas.dom.style.msTouchAction = 'none';
44173 this.mouse_btn_down = false;
44174 canvas.on('mousedown', this._handleMouseDown, this);
44175 canvas.on('mousemove', this._handleMouseMove, this);
44176 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44178 if (window.PointerEvent) {
44179 canvas.on('pointerdown', this._handleMouseDown, this);
44180 canvas.on('pointermove', this._handleMouseMove, this);
44181 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44184 if ('ontouchstart' in window) {
44185 canvas.on('touchstart', this._handleTouchStart, this);
44186 canvas.on('touchmove', this._handleTouchMove, this);
44187 canvas.on('touchend', this._handleTouchEnd, this);
44190 Roo.EventManager.onWindowResize(this.resize, this, true);
44192 // file input event
44193 this.fileEl().on('change', this.uploadImage, this);
44200 resize: function(){
44202 var canvas = this.canvasEl().dom;
44203 var ctx = this.canvasElCtx();
44204 var img_data = false;
44206 if(canvas.width > 0) {
44207 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44209 // setting canvas width will clean img data
44212 var style = window.getComputedStyle ?
44213 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44215 var padding_left = parseInt(style.paddingLeft) || 0;
44216 var padding_right = parseInt(style.paddingRight) || 0;
44218 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44221 ctx.putImageData(img_data, 0, 0);
44225 _handleMouseDown: function(e)
44227 if (e.browserEvent.which === 1) {
44228 this.mouse_btn_down = true;
44229 this.strokeBegin(e);
44233 _handleMouseMove: function (e)
44235 if (this.mouse_btn_down) {
44236 this.strokeMoveUpdate(e);
44240 _handleMouseUp: function (e)
44242 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44243 this.mouse_btn_down = false;
44248 _handleTouchStart: function (e) {
44250 e.preventDefault();
44251 if (e.browserEvent.targetTouches.length === 1) {
44252 // var touch = e.browserEvent.changedTouches[0];
44253 // this.strokeBegin(touch);
44255 this.strokeBegin(e); // assume e catching the correct xy...
44259 _handleTouchMove: function (e) {
44260 e.preventDefault();
44261 // var touch = event.targetTouches[0];
44262 // _this._strokeMoveUpdate(touch);
44263 this.strokeMoveUpdate(e);
44266 _handleTouchEnd: function (e) {
44267 var wasCanvasTouched = e.target === this.canvasEl().dom;
44268 if (wasCanvasTouched) {
44269 e.preventDefault();
44270 // var touch = event.changedTouches[0];
44271 // _this._strokeEnd(touch);
44276 reset: function () {
44277 this._lastPoints = [];
44278 this._lastVelocity = 0;
44279 this._lastWidth = (this.min_width + this.max_width) / 2;
44280 this.canvasElCtx().fillStyle = this.dot_color;
44283 strokeMoveUpdate: function(e)
44285 this.strokeUpdate(e);
44287 if (this.throttle) {
44288 this.throttleStroke(this.strokeUpdate, this.throttle);
44291 this.strokeUpdate(e);
44295 strokeBegin: function(e)
44297 var newPointGroup = {
44298 color: this.dot_color,
44302 if (typeof this.onBegin === 'function') {
44306 this.curve_data.push(newPointGroup);
44308 this.strokeUpdate(e);
44311 strokeUpdate: function(e)
44313 var rect = this.canvasEl().dom.getBoundingClientRect();
44314 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44315 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44316 var lastPoints = lastPointGroup.points;
44317 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44318 var isLastPointTooClose = lastPoint
44319 ? point.distanceTo(lastPoint) <= this.min_distance
44321 var color = lastPointGroup.color;
44322 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44323 var curve = this.addPoint(point);
44325 this.drawDot({color: color, point: point});
44328 this.drawCurve({color: color, curve: curve});
44338 strokeEnd: function(e)
44340 this.strokeUpdate(e);
44341 if (typeof this.onEnd === 'function') {
44346 addPoint: function (point) {
44347 var _lastPoints = this._lastPoints;
44348 _lastPoints.push(point);
44349 if (_lastPoints.length > 2) {
44350 if (_lastPoints.length === 3) {
44351 _lastPoints.unshift(_lastPoints[0]);
44353 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44354 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44355 _lastPoints.shift();
44361 calculateCurveWidths: function (startPoint, endPoint) {
44362 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44363 (1 - this.velocity_filter_weight) * this._lastVelocity;
44365 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44368 start: this._lastWidth
44371 this._lastVelocity = velocity;
44372 this._lastWidth = newWidth;
44376 drawDot: function (_a) {
44377 var color = _a.color, point = _a.point;
44378 var ctx = this.canvasElCtx();
44379 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44381 this.drawCurveSegment(point.x, point.y, width);
44383 ctx.fillStyle = color;
44387 drawCurve: function (_a) {
44388 var color = _a.color, curve = _a.curve;
44389 var ctx = this.canvasElCtx();
44390 var widthDelta = curve.endWidth - curve.startWidth;
44391 var drawSteps = Math.floor(curve.length()) * 2;
44393 ctx.fillStyle = color;
44394 for (var i = 0; i < drawSteps; i += 1) {
44395 var t = i / drawSteps;
44401 var x = uuu * curve.startPoint.x;
44402 x += 3 * uu * t * curve.control1.x;
44403 x += 3 * u * tt * curve.control2.x;
44404 x += ttt * curve.endPoint.x;
44405 var y = uuu * curve.startPoint.y;
44406 y += 3 * uu * t * curve.control1.y;
44407 y += 3 * u * tt * curve.control2.y;
44408 y += ttt * curve.endPoint.y;
44409 var width = curve.startWidth + ttt * widthDelta;
44410 this.drawCurveSegment(x, y, width);
44416 drawCurveSegment: function (x, y, width) {
44417 var ctx = this.canvasElCtx();
44419 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44420 this.is_empty = false;
44425 var ctx = this.canvasElCtx();
44426 var canvas = this.canvasEl().dom;
44427 ctx.fillStyle = this.bg_color;
44428 ctx.clearRect(0, 0, canvas.width, canvas.height);
44429 ctx.fillRect(0, 0, canvas.width, canvas.height);
44430 this.curve_data = [];
44432 this.is_empty = true;
44437 return this.el.select('input',true).first();
44440 canvasEl: function()
44442 return this.el.select('canvas',true).first();
44445 canvasElCtx: function()
44447 return this.el.select('canvas',true).first().dom.getContext('2d');
44450 getImage: function(type)
44452 if(this.is_empty) {
44457 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44460 drawFromImage: function(img_src)
44462 var img = new Image();
44464 img.onload = function(){
44465 this.canvasElCtx().drawImage(img, 0, 0);
44470 this.is_empty = false;
44473 selectImage: function()
44475 this.fileEl().dom.click();
44478 uploadImage: function(e)
44480 var reader = new FileReader();
44482 reader.onload = function(e){
44483 var img = new Image();
44484 img.onload = function(){
44486 this.canvasElCtx().drawImage(img, 0, 0);
44488 img.src = e.target.result;
44491 reader.readAsDataURL(e.target.files[0]);
44494 // Bezier Point Constructor
44495 Point: (function () {
44496 function Point(x, y, time) {
44499 this.time = time || Date.now();
44501 Point.prototype.distanceTo = function (start) {
44502 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44504 Point.prototype.equals = function (other) {
44505 return this.x === other.x && this.y === other.y && this.time === other.time;
44507 Point.prototype.velocityFrom = function (start) {
44508 return this.time !== start.time
44509 ? this.distanceTo(start) / (this.time - start.time)
44516 // Bezier Constructor
44517 Bezier: (function () {
44518 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44519 this.startPoint = startPoint;
44520 this.control2 = control2;
44521 this.control1 = control1;
44522 this.endPoint = endPoint;
44523 this.startWidth = startWidth;
44524 this.endWidth = endWidth;
44526 Bezier.fromPoints = function (points, widths, scope) {
44527 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44528 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44529 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44531 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44532 var dx1 = s1.x - s2.x;
44533 var dy1 = s1.y - s2.y;
44534 var dx2 = s2.x - s3.x;
44535 var dy2 = s2.y - s3.y;
44536 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44537 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44538 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44539 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44540 var dxm = m1.x - m2.x;
44541 var dym = m1.y - m2.y;
44542 var k = l2 / (l1 + l2);
44543 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44544 var tx = s2.x - cm.x;
44545 var ty = s2.y - cm.y;
44547 c1: new scope.Point(m1.x + tx, m1.y + ty),
44548 c2: new scope.Point(m2.x + tx, m2.y + ty)
44551 Bezier.prototype.length = function () {
44556 for (var i = 0; i <= steps; i += 1) {
44558 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44559 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44561 var xdiff = cx - px;
44562 var ydiff = cy - py;
44563 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44570 Bezier.prototype.point = function (t, start, c1, c2, end) {
44571 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44572 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44573 + (3.0 * c2 * (1.0 - t) * t * t)
44574 + (end * t * t * t);
44579 throttleStroke: function(fn, wait) {
44580 if (wait === void 0) { wait = 250; }
44582 var timeout = null;
44586 var later = function () {
44587 previous = Date.now();
44589 result = fn.apply(storedContext, storedArgs);
44591 storedContext = null;
44595 return function wrapper() {
44597 for (var _i = 0; _i < arguments.length; _i++) {
44598 args[_i] = arguments[_i];
44600 var now = Date.now();
44601 var remaining = wait - (now - previous);
44602 storedContext = this;
44604 if (remaining <= 0 || remaining > wait) {
44606 clearTimeout(timeout);
44610 result = fn.apply(storedContext, storedArgs);
44612 storedContext = null;
44616 else if (!timeout) {
44617 timeout = window.setTimeout(later, remaining);