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
655 * Create a new Element
656 * @param {Object} config The config object
659 Roo.bootstrap.Element = function(config){
660 Roo.bootstrap.Element.superclass.constructor.call(this, config);
666 * When a element is chick
667 * @param {Roo.bootstrap.Element} this
668 * @param {Roo.EventObject} e
676 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
681 preventDefault: false,
685 getAutoCreate : function(){
689 // cls: this.cls, double assign in parent class Component.js :: onRender
696 initEvents: function()
698 Roo.bootstrap.Element.superclass.initEvents.call(this);
701 this.el.on('click', this.onClick, this);
707 onClick : function(e)
709 if(this.preventDefault){
713 this.fireEvent('dblclick', this, e);
721 getValue : function()
723 return this.el.dom.innerHTML;
726 setValue : function(value)
728 this.el.dom.innerHTML = value;
743 * @class Roo.bootstrap.DropTarget
744 * @extends Roo.bootstrap.Element
745 * Bootstrap DropTarget class
747 * @cfg {string} name dropable name
750 * Create a new Dropable Area
751 * @param {Object} config The config object
754 Roo.bootstrap.DropTarget = function(config){
755 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
761 * When a element is chick
762 * @param {Roo.bootstrap.Element} this
763 * @param {Roo.EventObject} e
769 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
772 getAutoCreate : function(){
777 initEvents: function()
779 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
780 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
783 drop : this.dragDrop.createDelegate(this),
784 enter : this.dragEnter.createDelegate(this),
785 out : this.dragOut.createDelegate(this),
786 over : this.dragOver.createDelegate(this)
790 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
793 dragDrop : function(source,e,data)
795 // user has to decide how to impliment this.
798 //this.fireEvent('drop', this, source, e ,data);
802 dragEnter : function(n, dd, e, data)
804 // probably want to resize the element to match the dropped element..
806 this.originalSize = this.el.getSize();
807 this.el.setSize( n.el.getSize());
808 this.dropZone.DDM.refreshCache(this.name);
809 Roo.log([n, dd, e, data]);
812 dragOut : function(value)
814 // resize back to normal
816 this.el.setSize(this.originalSize);
817 this.dropZone.resetConstraints();
820 dragOver : function()
837 * @class Roo.bootstrap.Body
838 * @extends Roo.bootstrap.Component
839 * Bootstrap Body class
843 * @param {Object} config The config object
846 Roo.bootstrap.Body = function(config){
848 config = config || {};
850 Roo.bootstrap.Body.superclass.constructor.call(this, config);
851 this.el = Roo.get(config.el ? config.el : document.body );
852 if (this.cls && this.cls.length) {
853 Roo.get(document.body).addClass(this.cls);
857 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
859 is_body : true,// just to make sure it's constructed?
864 onRender : function(ct, position)
866 /* Roo.log("Roo.bootstrap.Body - onRender");
867 if (this.cls && this.cls.length) {
868 Roo.get(document.body).addClass(this.cls);
887 * @class Roo.bootstrap.ButtonGroup
888 * @extends Roo.bootstrap.Component
889 * Bootstrap ButtonGroup class
890 * @cfg {String} size lg | sm | xs (default empty normal)
891 * @cfg {String} align vertical | justified (default none)
892 * @cfg {String} direction up | down (default down)
893 * @cfg {Boolean} toolbar false | true
894 * @cfg {Boolean} btn true | false
899 * @param {Object} config The config object
902 Roo.bootstrap.ButtonGroup = function(config){
903 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
906 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
914 getAutoCreate : function(){
920 cfg.html = this.html || cfg.html;
931 if (['vertical','justified'].indexOf(this.align)!==-1) {
932 cfg.cls = 'btn-group-' + this.align;
934 if (this.align == 'justified') {
935 console.log(this.items);
939 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
940 cfg.cls += ' btn-group-' + this.size;
943 if (this.direction == 'up') {
944 cfg.cls += ' dropup' ;
950 * Add a button to the group (similar to NavItem API.)
952 addItem : function(cfg)
954 var cn = new Roo.bootstrap.Button(cfg);
956 cn.parentId = this.id;
957 cn.onRender(this.el, null);
971 * @class Roo.bootstrap.Button
972 * @extends Roo.bootstrap.Component
973 * Bootstrap Button class
974 * @cfg {String} html The button content
975 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
976 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
977 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
978 * @cfg {String} size (lg|sm|xs)
979 * @cfg {String} tag (a|input|submit)
980 * @cfg {String} href empty or href
981 * @cfg {Boolean} disabled default false;
982 * @cfg {Boolean} isClose default false;
983 * @cfg {String} glyphicon depricated - use fa
984 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
985 * @cfg {String} badge text for badge
986 * @cfg {String} theme (default|glow)
987 * @cfg {Boolean} inverse dark themed version
988 * @cfg {Boolean} toggle is it a slidy toggle button
989 * @cfg {Boolean} pressed default null - if the button ahs active state
990 * @cfg {String} ontext text for on slidy toggle state
991 * @cfg {String} offtext text for off slidy toggle state
992 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
993 * @cfg {Boolean} removeClass remove the standard class..
994 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
995 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
998 * Create a new button
999 * @param {Object} config The config object
1003 Roo.bootstrap.Button = function(config){
1004 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1010 * When a button is pressed
1011 * @param {Roo.bootstrap.Button} btn
1012 * @param {Roo.EventObject} e
1017 * When a button is double clicked
1018 * @param {Roo.bootstrap.Button} btn
1019 * @param {Roo.EventObject} e
1024 * After the button has been toggles
1025 * @param {Roo.bootstrap.Button} btn
1026 * @param {Roo.EventObject} e
1027 * @param {boolean} pressed (also available as button.pressed)
1033 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1054 preventDefault: true,
1063 getAutoCreate : function(){
1071 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1072 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1073 this.tag = 'button';
1077 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1079 if (this.toggle == true) {
1082 cls: 'slider-frame roo-button',
1086 'data-on-text':'ON',
1087 'data-off-text':'OFF',
1088 cls: 'slider-button',
1093 // why are we validating the weights?
1094 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1095 cfg.cls += ' ' + this.weight;
1102 cfg.cls += ' close';
1104 cfg["aria-hidden"] = true;
1106 cfg.html = "×";
1112 if (this.theme==='default') {
1113 cfg.cls = 'btn roo-button';
1115 //if (this.parentType != 'Navbar') {
1116 this.weight = this.weight.length ? this.weight : 'default';
1118 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1120 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1121 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1122 cfg.cls += ' btn-' + outline + weight;
1123 if (this.weight == 'default') {
1125 cfg.cls += ' btn-' + this.weight;
1128 } else if (this.theme==='glow') {
1131 cfg.cls = 'btn-glow roo-button';
1133 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1135 cfg.cls += ' ' + this.weight;
1141 this.cls += ' inverse';
1145 if (this.active || this.pressed === true) {
1146 cfg.cls += ' active';
1149 if (this.disabled) {
1150 cfg.disabled = 'disabled';
1154 Roo.log('changing to ul' );
1156 this.glyphicon = 'caret';
1157 if (Roo.bootstrap.version == 4) {
1158 this.fa = 'caret-down';
1163 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1165 //gsRoo.log(this.parentType);
1166 if (this.parentType === 'Navbar' && !this.parent().bar) {
1167 Roo.log('changing to li?');
1176 href : this.href || '#'
1179 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1180 cfg.cls += ' dropdown';
1187 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1189 if (this.glyphicon) {
1190 cfg.html = ' ' + cfg.html;
1195 cls: 'glyphicon glyphicon-' + this.glyphicon
1200 cfg.html = ' ' + cfg.html;
1205 cls: 'fa fas fa-' + this.fa
1215 // cfg.cls='btn roo-button';
1219 var value = cfg.html;
1224 cls: 'glyphicon glyphicon-' + this.glyphicon,
1231 cls: 'fa fas fa-' + this.fa,
1236 var bw = this.badge_weight.length ? this.badge_weight :
1237 (this.weight.length ? this.weight : 'secondary');
1238 bw = bw == 'default' ? 'secondary' : bw;
1244 cls: 'badge badge-' + bw,
1253 cfg.cls += ' dropdown';
1254 cfg.html = typeof(cfg.html) != 'undefined' ?
1255 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1258 if (cfg.tag !== 'a' && this.href !== '') {
1259 throw "Tag must be a to set href.";
1260 } else if (this.href.length > 0) {
1261 cfg.href = this.href;
1264 if(this.removeClass){
1269 cfg.target = this.target;
1274 initEvents: function() {
1275 // Roo.log('init events?');
1276 // Roo.log(this.el.dom);
1279 if (typeof (this.menu) != 'undefined') {
1280 this.menu.parentType = this.xtype;
1281 this.menu.triggerEl = this.el;
1282 this.addxtype(Roo.apply({}, this.menu));
1286 if (this.el.hasClass('roo-button')) {
1287 this.el.on('click', this.onClick, this);
1288 this.el.on('dblclick', this.onDblClick, this);
1290 this.el.select('.roo-button').on('click', this.onClick, this);
1291 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1295 if(this.removeClass){
1296 this.el.on('click', this.onClick, this);
1299 if (this.group === true) {
1300 if (this.pressed === false || this.pressed === true) {
1303 this.pressed = false;
1304 this.setActive(this.pressed);
1309 this.el.enableDisplayMode();
1312 onClick : function(e)
1314 if (this.disabled) {
1318 Roo.log('button on click ');
1319 if(this.preventDefault){
1328 this.setActive(true);
1329 var pi = this.parent().items;
1330 for (var i = 0;i < pi.length;i++) {
1331 if (this == pi[i]) {
1334 if (pi[i].el.hasClass('roo-button')) {
1335 pi[i].setActive(false);
1338 this.fireEvent('click', this, e);
1342 if (this.pressed === true || this.pressed === false) {
1343 this.toggleActive(e);
1347 this.fireEvent('click', this, e);
1349 onDblClick: function(e)
1351 if (this.disabled) {
1354 if(this.preventDefault){
1357 this.fireEvent('dblclick', this, e);
1360 * Enables this button
1364 this.disabled = false;
1365 this.el.removeClass('disabled');
1366 this.el.dom.removeAttribute("disabled");
1370 * Disable this button
1372 disable : function()
1374 this.disabled = true;
1375 this.el.addClass('disabled');
1376 this.el.attr("disabled", "disabled")
1379 * sets the active state on/off,
1380 * @param {Boolean} state (optional) Force a particular state
1382 setActive : function(v) {
1384 this.el[v ? 'addClass' : 'removeClass']('active');
1388 * toggles the current active state
1390 toggleActive : function(e)
1392 this.setActive(!this.pressed); // this modifies pressed...
1393 this.fireEvent('toggle', this, e, this.pressed);
1396 * get the current active state
1397 * @return {boolean} true if it's active
1399 isActive : function()
1401 return this.el.hasClass('active');
1404 * set the text of the first selected button
1406 setText : function(str)
1408 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1411 * get the text of the first selected button
1413 getText : function()
1415 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1418 setWeight : function(str)
1420 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1421 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1423 var outline = this.outline ? 'outline-' : '';
1424 if (str == 'default') {
1425 this.el.addClass('btn-default btn-outline-secondary');
1428 this.el.addClass('btn-' + outline + str);
1433 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1435 Roo.bootstrap.Button.weights = [
1455 * @class Roo.bootstrap.Column
1456 * @extends Roo.bootstrap.Component
1457 * Bootstrap Column class
1458 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1459 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1460 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1461 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1462 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1463 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1464 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1465 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1468 * @cfg {Boolean} hidden (true|false) hide the element
1469 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1470 * @cfg {String} fa (ban|check|...) font awesome icon
1471 * @cfg {Number} fasize (1|2|....) font awsome size
1473 * @cfg {String} icon (info-sign|check|...) glyphicon name
1475 * @cfg {String} html content of column.
1478 * Create a new Column
1479 * @param {Object} config The config object
1482 Roo.bootstrap.Column = function(config){
1483 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1486 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1504 getAutoCreate : function(){
1505 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1513 var sizes = ['xs','sm','md','lg'];
1514 sizes.map(function(size ,ix){
1515 //Roo.log( size + ':' + settings[size]);
1517 if (settings[size+'off'] !== false) {
1518 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1521 if (settings[size] === false) {
1525 if (!settings[size]) { // 0 = hidden
1526 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1528 for (var i = ix; i > -1; i--) {
1529 cfg.cls += ' d-' + sizes[i] + '-none';
1535 cfg.cls += ' col-' + size + '-' + settings[size] + (
1536 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1542 cfg.cls += ' hidden';
1545 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1546 cfg.cls +=' alert alert-' + this.alert;
1550 if (this.html.length) {
1551 cfg.html = this.html;
1555 if (this.fasize > 1) {
1556 fasize = ' fa-' + this.fasize + 'x';
1558 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1563 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1582 * @class Roo.bootstrap.Container
1583 * @extends Roo.bootstrap.Component
1584 * Bootstrap Container class
1585 * @cfg {Boolean} jumbotron is it a jumbotron element
1586 * @cfg {String} html content of element
1587 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1588 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1589 * @cfg {String} header content of header (for panel)
1590 * @cfg {String} footer content of footer (for panel)
1591 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1592 * @cfg {String} tag (header|aside|section) type of HTML tag.
1593 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1594 * @cfg {String} fa font awesome icon
1595 * @cfg {String} icon (info-sign|check|...) glyphicon name
1596 * @cfg {Boolean} hidden (true|false) hide the element
1597 * @cfg {Boolean} expandable (true|false) default false
1598 * @cfg {Boolean} expanded (true|false) default true
1599 * @cfg {String} rheader contet on the right of header
1600 * @cfg {Boolean} clickable (true|false) default false
1604 * Create a new Container
1605 * @param {Object} config The config object
1608 Roo.bootstrap.Container = function(config){
1609 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1615 * After the panel has been expand
1617 * @param {Roo.bootstrap.Container} this
1622 * After the panel has been collapsed
1624 * @param {Roo.bootstrap.Container} this
1629 * When a element is chick
1630 * @param {Roo.bootstrap.Container} this
1631 * @param {Roo.EventObject} e
1637 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1655 getChildContainer : function() {
1661 if (this.panel.length) {
1662 return this.el.select('.panel-body',true).first();
1669 getAutoCreate : function(){
1672 tag : this.tag || 'div',
1676 if (this.jumbotron) {
1677 cfg.cls = 'jumbotron';
1682 // - this is applied by the parent..
1684 // cfg.cls = this.cls + '';
1687 if (this.sticky.length) {
1689 var bd = Roo.get(document.body);
1690 if (!bd.hasClass('bootstrap-sticky')) {
1691 bd.addClass('bootstrap-sticky');
1692 Roo.select('html',true).setStyle('height', '100%');
1695 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1699 if (this.well.length) {
1700 switch (this.well) {
1703 cfg.cls +=' well well-' +this.well;
1712 cfg.cls += ' hidden';
1716 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1717 cfg.cls +=' alert alert-' + this.alert;
1722 if (this.panel.length) {
1723 cfg.cls += ' panel panel-' + this.panel;
1725 if (this.header.length) {
1729 if(this.expandable){
1731 cfg.cls = cfg.cls + ' expandable';
1735 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1743 cls : 'panel-title',
1744 html : (this.expandable ? ' ' : '') + this.header
1748 cls: 'panel-header-right',
1754 cls : 'panel-heading',
1755 style : this.expandable ? 'cursor: pointer' : '',
1763 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1768 if (this.footer.length) {
1770 cls : 'panel-footer',
1779 body.html = this.html || cfg.html;
1780 // prefix with the icons..
1782 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1785 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1790 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1791 cfg.cls = 'container';
1797 initEvents: function()
1799 if(this.expandable){
1800 var headerEl = this.headerEl();
1803 headerEl.on('click', this.onToggleClick, this);
1808 this.el.on('click', this.onClick, this);
1813 onToggleClick : function()
1815 var headerEl = this.headerEl();
1831 if(this.fireEvent('expand', this)) {
1833 this.expanded = true;
1835 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1837 this.el.select('.panel-body',true).first().removeClass('hide');
1839 var toggleEl = this.toggleEl();
1845 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1850 collapse : function()
1852 if(this.fireEvent('collapse', this)) {
1854 this.expanded = false;
1856 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1857 this.el.select('.panel-body',true).first().addClass('hide');
1859 var toggleEl = this.toggleEl();
1865 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1869 toggleEl : function()
1871 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1875 return this.el.select('.panel-heading .fa',true).first();
1878 headerEl : function()
1880 if(!this.el || !this.panel.length || !this.header.length){
1884 return this.el.select('.panel-heading',true).first()
1889 if(!this.el || !this.panel.length){
1893 return this.el.select('.panel-body',true).first()
1896 titleEl : function()
1898 if(!this.el || !this.panel.length || !this.header.length){
1902 return this.el.select('.panel-title',true).first();
1905 setTitle : function(v)
1907 var titleEl = this.titleEl();
1913 titleEl.dom.innerHTML = v;
1916 getTitle : function()
1919 var titleEl = this.titleEl();
1925 return titleEl.dom.innerHTML;
1928 setRightTitle : function(v)
1930 var t = this.el.select('.panel-header-right',true).first();
1936 t.dom.innerHTML = v;
1939 onClick : function(e)
1943 this.fireEvent('click', this, e);
1950 * This is BS4's Card element.. - similar to our containers probably..
1954 * @class Roo.bootstrap.Card
1955 * @extends Roo.bootstrap.Component
1956 * Bootstrap Card class
1959 * possible... may not be implemented..
1960 * @cfg {String} header_image src url of image.
1961 * @cfg {String|Object} header
1962 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1963 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1965 * @cfg {String} title
1966 * @cfg {String} subtitle
1967 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1968 * @cfg {String} footer
1970 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1972 * @cfg {String} margin (0|1|2|3|4|5|auto)
1973 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1974 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1975 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1976 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1977 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1978 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1980 * @cfg {String} padding (0|1|2|3|4|5)
1981 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1982 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1983 * @cfg {String} padding_left (0|1|2|3|4|5)
1984 * @cfg {String} padding_right (0|1|2|3|4|5)
1985 * @cfg {String} padding_x (0|1|2|3|4|5)
1986 * @cfg {String} padding_y (0|1|2|3|4|5)
1988 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1989 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1990 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1991 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1992 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1994 * @config {Boolean} dragable if this card can be dragged.
1995 * @config {String} drag_group group for drag
1996 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
1997 * @config {String} drop_group group for drag
1999 * @config {Boolean} collapsable can the body be collapsed.
2000 * @config {Boolean} collapsed is the body collapsed when rendered...
2001 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2002 * @config {Boolean} rotated is the body rotated when rendered...
2005 * Create a new Container
2006 * @param {Object} config The config object
2009 Roo.bootstrap.Card = function(config){
2010 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2016 * When a element a card is dropped
2017 * @param {Roo.bootstrap.Card} this
2020 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2021 * @param {String} position 'above' or 'below'
2022 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2028 * When a element a card is rotate
2029 * @param {Roo.bootstrap.Card} this
2030 * @param {Roo.Element} n the node being dropped?
2031 * @param {Boolean} rotate status
2036 * When a card element is dragged over ready to drop (return false to block dropable)
2037 * @param {Roo.bootstrap.Card} this
2038 * @param {Object} data from dragdrop
2046 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2051 margin: '', /// may be better in component?
2081 collapsable : false,
2090 childContainer : false,
2091 dropEl : false, /// the dom placeholde element that indicates drop location.
2092 containerEl: false, // body container
2093 bodyEl: false, // card-body
2094 headerContainerEl : false, //
2096 header_imageEl : false,
2098 layoutCls : function()
2102 Roo.log(this.margin_bottom.length);
2103 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2104 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2106 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2107 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2109 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2110 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2114 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2115 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2116 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2120 // more generic support?
2128 // Roo.log("Call onRender: " + this.xtype);
2129 /* We are looking at something like this.
2131 <img src="..." class="card-img-top" alt="...">
2132 <div class="card-body">
2133 <h5 class="card-title">Card title</h5>
2134 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2136 >> this bit is really the body...
2137 <div> << we will ad dthis in hopefully it will not break shit.
2139 ** card text does not actually have any styling...
2141 <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>
2144 <a href="#" class="card-link">Card link</a>
2147 <div class="card-footer">
2148 <small class="text-muted">Last updated 3 mins ago</small>
2152 getAutoCreate : function(){
2160 if (this.weight.length && this.weight != 'light') {
2161 cfg.cls += ' text-white';
2163 cfg.cls += ' text-dark'; // need as it's nested..
2165 if (this.weight.length) {
2166 cfg.cls += ' bg-' + this.weight;
2169 cfg.cls += ' ' + this.layoutCls();
2172 var hdr_ctr = false;
2173 if (this.header.length) {
2175 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2176 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2184 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2190 if (this.collapsable) {
2193 cls : 'd-block user-select-none',
2197 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2202 hdr.cn.push(hdr_ctr);
2207 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2212 if (this.header_image.length) {
2215 cls : 'card-img-top',
2216 src: this.header_image // escape?
2221 cls : 'card-img-top d-none'
2227 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2231 if (this.collapsable || this.rotateable) {
2234 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2241 if (this.title.length) {
2245 src: this.title // escape?
2249 if (this.subtitle.length) {
2253 src: this.subtitle // escape?
2259 cls : 'roo-card-body-ctr'
2262 if (this.html.length) {
2268 // fixme ? handle objects?
2270 if (this.footer.length) {
2273 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2278 cfg.cn.push({cls : 'card-footer d-none'});
2287 getCardHeader : function()
2289 var ret = this.el.select('.card-header',true).first();
2290 if (ret.hasClass('d-none')) {
2291 ret.removeClass('d-none');
2296 getCardFooter : function()
2298 var ret = this.el.select('.card-footer',true).first();
2299 if (ret.hasClass('d-none')) {
2300 ret.removeClass('d-none');
2305 getCardImageTop : function()
2307 var ret = this.header_imageEl;
2308 if (ret.hasClass('d-none')) {
2309 ret.removeClass('d-none');
2315 getChildContainer : function()
2321 return this.el.select('.roo-card-body-ctr',true).first();
2324 initEvents: function()
2326 this.bodyEl = this.el.select('.card-body',true).first();
2327 this.containerEl = this.getChildContainer();
2329 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2330 containerScroll: true,
2331 ddGroup: this.drag_group || 'default_card_drag_group'
2333 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2335 if (this.dropable) {
2336 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2337 containerScroll: true,
2338 ddGroup: this.drop_group || 'default_card_drag_group'
2340 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2341 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2342 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2343 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2344 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2347 if (this.collapsable) {
2348 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2350 if (this.rotateable) {
2351 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2353 this.collapsableEl = this.el.select('.roo-collapsable').first();
2355 this.footerEl = this.el.select('.card-footer').first();
2356 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2357 this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2358 this.headerEl = this.el.select('.card-header',true).first();
2361 this.el.addClass('roo-card-rotated');
2362 this.fireEvent('rotate', this, true);
2364 this.header_imageEl = this.el.select('.card-img-top',true).first();
2365 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2368 getDragData : function(e)
2370 var target = this.getEl();
2372 //this.handleSelection(e);
2377 nodes: this.getEl(),
2382 dragData.ddel = target.dom ; // the div element
2383 Roo.log(target.getWidth( ));
2384 dragData.ddel.style.width = target.getWidth() + 'px';
2391 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2392 * whole Element becomes the target, and this causes the drop gesture to append.
2394 * Returns an object:
2397 position : 'below' or 'above'
2398 card : relateive to card OBJECT (or true for no cards listed)
2399 items_n : relative to nth item in list
2400 card_n : relative to nth card in list
2405 getTargetFromEvent : function(e, dragged_card_el)
2407 var target = e.getTarget();
2408 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2409 target = target.parentNode;
2420 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2421 // see if target is one of the 'cards'...
2424 //Roo.log(this.items.length);
2427 var last_card_n = 0;
2429 for (var i = 0;i< this.items.length;i++) {
2431 if (!this.items[i].el.hasClass('card')) {
2434 pos = this.getDropPoint(e, this.items[i].el.dom);
2436 cards_len = ret.cards.length;
2437 //Roo.log(this.items[i].el.dom.id);
2438 ret.cards.push(this.items[i]);
2440 if (ret.card_n < 0 && pos == 'above') {
2441 ret.position = cards_len > 0 ? 'below' : pos;
2442 ret.items_n = i > 0 ? i - 1 : 0;
2443 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2444 ret.card = ret.cards[ret.card_n];
2447 if (!ret.cards.length) {
2449 ret.position = 'below';
2453 // could not find a card.. stick it at the end..
2454 if (ret.card_n < 0) {
2455 ret.card_n = last_card_n;
2456 ret.card = ret.cards[last_card_n];
2457 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2458 ret.position = 'below';
2461 if (this.items[ret.items_n].el == dragged_card_el) {
2465 if (ret.position == 'below') {
2466 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2468 if (card_after && card_after.el == dragged_card_el) {
2475 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2477 if (card_before && card_before.el == dragged_card_el) {
2484 onNodeEnter : function(n, dd, e, data){
2487 onNodeOver : function(n, dd, e, data)
2490 var target_info = this.getTargetFromEvent(e,data.source.el);
2491 if (target_info === false) {
2492 this.dropPlaceHolder('hide');
2495 Roo.log(['getTargetFromEvent', target_info ]);
2498 if (this.fireEvent('cardover', this, [ data ]) === false) {
2502 this.dropPlaceHolder('show', target_info,data);
2506 onNodeOut : function(n, dd, e, data){
2507 this.dropPlaceHolder('hide');
2510 onNodeDrop : function(n, dd, e, data)
2513 // call drop - return false if
2515 // this could actually fail - if the Network drops..
2516 // we will ignore this at present..- client should probably reload
2517 // the whole set of cards if stuff like that fails.
2520 var info = this.getTargetFromEvent(e,data.source.el);
2521 if (info === false) {
2524 this.dropPlaceHolder('hide');
2528 this.acceptCard(data.source, info.position, info.card, info.items_n);
2532 firstChildCard : function()
2534 for (var i = 0;i< this.items.length;i++) {
2536 if (!this.items[i].el.hasClass('card')) {
2539 return this.items[i];
2541 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2546 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2548 acceptCard : function(move_card, position, next_to_card )
2550 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2554 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2556 move_card.parent().removeCard(move_card);
2559 var dom = move_card.el.dom;
2560 dom.style.width = ''; // clear with - which is set by drag.
2562 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2563 var cardel = next_to_card.el.dom;
2565 if (position == 'above' ) {
2566 cardel.parentNode.insertBefore(dom, cardel);
2567 } else if (cardel.nextSibling) {
2568 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2570 cardel.parentNode.append(dom);
2573 // card container???
2574 this.containerEl.dom.append(dom);
2577 //FIXME HANDLE card = true
2579 // add this to the correct place in items.
2581 // remove Card from items.
2584 if (this.items.length) {
2586 //Roo.log([info.items_n, info.position, this.items.length]);
2587 for (var i =0; i < this.items.length; i++) {
2588 if (i == to_items_n && position == 'above') {
2589 nitems.push(move_card);
2591 nitems.push(this.items[i]);
2592 if (i == to_items_n && position == 'below') {
2593 nitems.push(move_card);
2596 this.items = nitems;
2597 Roo.log(this.items);
2599 this.items.push(move_card);
2602 move_card.parentId = this.id;
2608 removeCard : function(c)
2610 this.items = this.items.filter(function(e) { return e != c });
2613 dom.parentNode.removeChild(dom);
2614 dom.style.width = ''; // clear with - which is set by drag.
2619 /** Decide whether to drop above or below a View node. */
2620 getDropPoint : function(e, n, dd)
2625 if (n == this.containerEl.dom) {
2628 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2629 var c = t + (b - t) / 2;
2630 var y = Roo.lib.Event.getPageY(e);
2637 onToggleCollapse : function(e)
2639 if (this.collapsed) {
2640 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2641 this.collapsableEl.addClass('show');
2642 this.collapsed = false;
2645 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2646 this.collapsableEl.removeClass('show');
2647 this.collapsed = true;
2652 onToggleRotate : function(e)
2654 this.collapsableEl.removeClass('show');
2655 this.footerEl.removeClass('d-none');
2656 this.el.removeClass('roo-card-rotated');
2657 this.el.removeClass('d-none');
2660 this.collapsableEl.addClass('show');
2661 this.rotated = false;
2662 this.fireEvent('rotate', this, this.rotated);
2665 this.el.addClass('roo-card-rotated');
2666 this.footerEl.addClass('d-none');
2667 this.el.select('.roo-collapsable').removeClass('show');
2669 this.rotated = true;
2670 this.fireEvent('rotate', this, this.rotated);
2674 dropPlaceHolder: function (action, info, data)
2676 if (this.dropEl === false) {
2677 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2681 this.dropEl.removeClass(['d-none', 'd-block']);
2682 if (action == 'hide') {
2684 this.dropEl.addClass('d-none');
2687 // FIXME - info.card == true!!!
2688 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2690 if (info.card !== true) {
2691 var cardel = info.card.el.dom;
2693 if (info.position == 'above') {
2694 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2695 } else if (cardel.nextSibling) {
2696 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2698 cardel.parentNode.append(this.dropEl.dom);
2701 // card container???
2702 this.containerEl.dom.append(this.dropEl.dom);
2705 this.dropEl.addClass('d-block roo-card-dropzone');
2707 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2714 setHeaderText: function(html)
2717 if (this.headerContainerEl) {
2718 this.headerContainerEl.dom.innerHTML = html;
2721 onHeaderImageLoad : function(ev, he)
2723 if (!this.header_image_fit_square) {
2727 var hw = he.naturalHeight / he.naturalWidth;
2730 //var w = he.dom.naturalWidth;
2733 he.style.position = 'relative';
2735 var nw = (ww * (1/hw));
2736 Roo.get(he).setSize( ww * (1/hw), ww);
2737 he.style.left = ((ww - nw)/ 2) + 'px';
2738 he.style.position = 'relative';
2749 * Card header - holder for the card header elements.
2754 * @class Roo.bootstrap.CardHeader
2755 * @extends Roo.bootstrap.Element
2756 * Bootstrap CardHeader class
2758 * Create a new Card Header - that you can embed children into
2759 * @param {Object} config The config object
2762 Roo.bootstrap.CardHeader = function(config){
2763 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2766 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2769 container_method : 'getCardHeader'
2782 * Card footer - holder for the card footer elements.
2787 * @class Roo.bootstrap.CardFooter
2788 * @extends Roo.bootstrap.Element
2789 * Bootstrap CardFooter class
2791 * Create a new Card Footer - that you can embed children into
2792 * @param {Object} config The config object
2795 Roo.bootstrap.CardFooter = function(config){
2796 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2799 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2802 container_method : 'getCardFooter'
2815 * Card header - holder for the card header elements.
2820 * @class Roo.bootstrap.CardImageTop
2821 * @extends Roo.bootstrap.Element
2822 * Bootstrap CardImageTop class
2824 * Create a new Card Image Top container
2825 * @param {Object} config The config object
2828 Roo.bootstrap.CardImageTop = function(config){
2829 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2832 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2835 container_method : 'getCardImageTop'
2853 * @class Roo.bootstrap.Img
2854 * @extends Roo.bootstrap.Component
2855 * Bootstrap Img class
2856 * @cfg {Boolean} imgResponsive false | true
2857 * @cfg {String} border rounded | circle | thumbnail
2858 * @cfg {String} src image source
2859 * @cfg {String} alt image alternative text
2860 * @cfg {String} href a tag href
2861 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2862 * @cfg {String} xsUrl xs image source
2863 * @cfg {String} smUrl sm image source
2864 * @cfg {String} mdUrl md image source
2865 * @cfg {String} lgUrl lg image source
2868 * Create a new Input
2869 * @param {Object} config The config object
2872 Roo.bootstrap.Img = function(config){
2873 Roo.bootstrap.Img.superclass.constructor.call(this, config);
2879 * The img click event for the img.
2880 * @param {Roo.EventObject} e
2886 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
2888 imgResponsive: true,
2898 getAutoCreate : function()
2900 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2901 return this.createSingleImg();
2906 cls: 'roo-image-responsive-group',
2911 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2913 if(!_this[size + 'Url']){
2919 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2920 html: _this.html || cfg.html,
2921 src: _this[size + 'Url']
2924 img.cls += ' roo-image-responsive-' + size;
2926 var s = ['xs', 'sm', 'md', 'lg'];
2928 s.splice(s.indexOf(size), 1);
2930 Roo.each(s, function(ss){
2931 img.cls += ' hidden-' + ss;
2934 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2935 cfg.cls += ' img-' + _this.border;
2939 cfg.alt = _this.alt;
2952 a.target = _this.target;
2956 cfg.cn.push((_this.href) ? a : img);
2963 createSingleImg : function()
2967 cls: (this.imgResponsive) ? 'img-responsive' : '',
2969 src : 'about:blank' // just incase src get's set to undefined?!?
2972 cfg.html = this.html || cfg.html;
2974 cfg.src = this.src || cfg.src;
2976 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2977 cfg.cls += ' img-' + this.border;
2994 a.target = this.target;
2999 return (this.href) ? a : cfg;
3002 initEvents: function()
3005 this.el.on('click', this.onClick, this);
3010 onClick : function(e)
3012 Roo.log('img onclick');
3013 this.fireEvent('click', this, e);
3016 * Sets the url of the image - used to update it
3017 * @param {String} url the url of the image
3020 setSrc : function(url)
3024 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3025 this.el.dom.src = url;
3029 this.el.select('img', true).first().dom.src = url;
3045 * @class Roo.bootstrap.Link
3046 * @extends Roo.bootstrap.Component
3047 * Bootstrap Link Class
3048 * @cfg {String} alt image alternative text
3049 * @cfg {String} href a tag href
3050 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3051 * @cfg {String} html the content of the link.
3052 * @cfg {String} anchor name for the anchor link
3053 * @cfg {String} fa - favicon
3055 * @cfg {Boolean} preventDefault (true | false) default false
3059 * Create a new Input
3060 * @param {Object} config The config object
3063 Roo.bootstrap.Link = function(config){
3064 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3070 * The img click event for the img.
3071 * @param {Roo.EventObject} e
3077 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3081 preventDefault: false,
3087 getAutoCreate : function()
3089 var html = this.html || '';
3091 if (this.fa !== false) {
3092 html = '<i class="fa fa-' + this.fa + '"></i>';
3097 // anchor's do not require html/href...
3098 if (this.anchor === false) {
3100 cfg.href = this.href || '#';
3102 cfg.name = this.anchor;
3103 if (this.html !== false || this.fa !== false) {
3106 if (this.href !== false) {
3107 cfg.href = this.href;
3111 if(this.alt !== false){
3116 if(this.target !== false) {
3117 cfg.target = this.target;
3123 initEvents: function() {
3125 if(!this.href || this.preventDefault){
3126 this.el.on('click', this.onClick, this);
3130 onClick : function(e)
3132 if(this.preventDefault){
3135 //Roo.log('img onclick');
3136 this.fireEvent('click', this, e);
3149 * @class Roo.bootstrap.Header
3150 * @extends Roo.bootstrap.Component
3151 * Bootstrap Header class
3152 * @cfg {String} html content of header
3153 * @cfg {Number} level (1|2|3|4|5|6) default 1
3156 * Create a new Header
3157 * @param {Object} config The config object
3161 Roo.bootstrap.Header = function(config){
3162 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3165 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3173 getAutoCreate : function(){
3178 tag: 'h' + (1 *this.level),
3179 html: this.html || ''
3191 * Ext JS Library 1.1.1
3192 * Copyright(c) 2006-2007, Ext JS, LLC.
3194 * Originally Released Under LGPL - original licence link has changed is not relivant.
3197 * <script type="text/javascript">
3201 * @class Roo.bootstrap.MenuMgr
3202 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3205 Roo.bootstrap.MenuMgr = function(){
3206 var menus, active, groups = {}, attached = false, lastShow = new Date();
3208 // private - called when first menu is created
3211 active = new Roo.util.MixedCollection();
3212 Roo.get(document).addKeyListener(27, function(){
3213 if(active.length > 0){
3221 if(active && active.length > 0){
3222 var c = active.clone();
3232 if(active.length < 1){
3233 Roo.get(document).un("mouseup", onMouseDown);
3241 var last = active.last();
3242 lastShow = new Date();
3245 Roo.get(document).on("mouseup", onMouseDown);
3250 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3251 m.parentMenu.activeChild = m;
3252 }else if(last && last.isVisible()){
3253 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3258 function onBeforeHide(m){
3260 m.activeChild.hide();
3262 if(m.autoHideTimer){
3263 clearTimeout(m.autoHideTimer);
3264 delete m.autoHideTimer;
3269 function onBeforeShow(m){
3270 var pm = m.parentMenu;
3271 if(!pm && !m.allowOtherMenus){
3273 }else if(pm && pm.activeChild && active != m){
3274 pm.activeChild.hide();
3278 // private this should really trigger on mouseup..
3279 function onMouseDown(e){
3280 Roo.log("on Mouse Up");
3282 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3283 Roo.log("MenuManager hideAll");
3292 function onBeforeCheck(mi, state){
3294 var g = groups[mi.group];
3295 for(var i = 0, l = g.length; i < l; i++){
3297 g[i].setChecked(false);
3306 * Hides all menus that are currently visible
3308 hideAll : function(){
3313 register : function(menu){
3317 menus[menu.id] = menu;
3318 menu.on("beforehide", onBeforeHide);
3319 menu.on("hide", onHide);
3320 menu.on("beforeshow", onBeforeShow);
3321 menu.on("show", onShow);
3323 if(g && menu.events["checkchange"]){
3327 groups[g].push(menu);
3328 menu.on("checkchange", onCheck);
3333 * Returns a {@link Roo.menu.Menu} object
3334 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3335 * be used to generate and return a new Menu instance.
3337 get : function(menu){
3338 if(typeof menu == "string"){ // menu id
3340 }else if(menu.events){ // menu instance
3343 /*else if(typeof menu.length == 'number'){ // array of menu items?
3344 return new Roo.bootstrap.Menu({items:menu});
3345 }else{ // otherwise, must be a config
3346 return new Roo.bootstrap.Menu(menu);
3353 unregister : function(menu){
3354 delete menus[menu.id];
3355 menu.un("beforehide", onBeforeHide);
3356 menu.un("hide", onHide);
3357 menu.un("beforeshow", onBeforeShow);
3358 menu.un("show", onShow);
3360 if(g && menu.events["checkchange"]){
3361 groups[g].remove(menu);
3362 menu.un("checkchange", onCheck);
3367 registerCheckable : function(menuItem){
3368 var g = menuItem.group;
3373 groups[g].push(menuItem);
3374 menuItem.on("beforecheckchange", onBeforeCheck);
3379 unregisterCheckable : function(menuItem){
3380 var g = menuItem.group;
3382 groups[g].remove(menuItem);
3383 menuItem.un("beforecheckchange", onBeforeCheck);
3395 * @class Roo.bootstrap.Menu
3396 * @extends Roo.bootstrap.Component
3397 * Bootstrap Menu class - container for MenuItems
3398 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3399 * @cfg {bool} hidden if the menu should be hidden when rendered.
3400 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3401 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3405 * @param {Object} config The config object
3409 Roo.bootstrap.Menu = function(config){
3410 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3411 if (this.registerMenu && this.type != 'treeview') {
3412 Roo.bootstrap.MenuMgr.register(this);
3419 * Fires before this menu is displayed (return false to block)
3420 * @param {Roo.menu.Menu} this
3425 * Fires before this menu is hidden (return false to block)
3426 * @param {Roo.menu.Menu} this
3431 * Fires after this menu is displayed
3432 * @param {Roo.menu.Menu} this
3437 * Fires after this menu is hidden
3438 * @param {Roo.menu.Menu} this
3443 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3444 * @param {Roo.menu.Menu} this
3445 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3446 * @param {Roo.EventObject} e
3451 * Fires when the mouse is hovering over this menu
3452 * @param {Roo.menu.Menu} this
3453 * @param {Roo.EventObject} e
3454 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3459 * Fires when the mouse exits this menu
3460 * @param {Roo.menu.Menu} this
3461 * @param {Roo.EventObject} e
3462 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3467 * Fires when a menu item contained in this menu is clicked
3468 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3469 * @param {Roo.EventObject} e
3473 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3476 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3480 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3483 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3485 registerMenu : true,
3487 menuItems :false, // stores the menu items..
3497 getChildContainer : function() {
3501 getAutoCreate : function(){
3503 //if (['right'].indexOf(this.align)!==-1) {
3504 // cfg.cn[1].cls += ' pull-right'
3510 cls : 'dropdown-menu' ,
3511 style : 'z-index:1000'
3515 if (this.type === 'submenu') {
3516 cfg.cls = 'submenu active';
3518 if (this.type === 'treeview') {
3519 cfg.cls = 'treeview-menu';
3524 initEvents : function() {
3526 // Roo.log("ADD event");
3527 // Roo.log(this.triggerEl.dom);
3529 this.triggerEl.on('click', this.onTriggerClick, this);
3531 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3534 if (this.triggerEl.hasClass('nav-item')) {
3535 // dropdown toggle on the 'a' in BS4?
3536 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3538 this.triggerEl.addClass('dropdown-toggle');
3541 this.el.on('touchstart' , this.onTouch, this);
3543 this.el.on('click' , this.onClick, this);
3545 this.el.on("mouseover", this.onMouseOver, this);
3546 this.el.on("mouseout", this.onMouseOut, this);
3550 findTargetItem : function(e)
3552 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3556 //Roo.log(t); Roo.log(t.id);
3558 //Roo.log(this.menuitems);
3559 return this.menuitems.get(t.id);
3561 //return this.items.get(t.menuItemId);
3567 onTouch : function(e)
3569 Roo.log("menu.onTouch");
3570 //e.stopEvent(); this make the user popdown broken
3574 onClick : function(e)
3576 Roo.log("menu.onClick");
3578 var t = this.findTargetItem(e);
3579 if(!t || t.isContainer){
3584 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3585 if(t == this.activeItem && t.shouldDeactivate(e)){
3586 this.activeItem.deactivate();
3587 delete this.activeItem;
3591 this.setActiveItem(t, true);
3599 Roo.log('pass click event');
3603 this.fireEvent("click", this, t, e);
3607 if(!t.href.length || t.href == '#'){
3608 (function() { _this.hide(); }).defer(100);
3613 onMouseOver : function(e){
3614 var t = this.findTargetItem(e);
3617 // if(t.canActivate && !t.disabled){
3618 // this.setActiveItem(t, true);
3622 this.fireEvent("mouseover", this, e, t);
3624 isVisible : function(){
3625 return !this.hidden;
3627 onMouseOut : function(e){
3628 var t = this.findTargetItem(e);
3631 // if(t == this.activeItem && t.shouldDeactivate(e)){
3632 // this.activeItem.deactivate();
3633 // delete this.activeItem;
3636 this.fireEvent("mouseout", this, e, t);
3641 * Displays this menu relative to another element
3642 * @param {String/HTMLElement/Roo.Element} element The element to align to
3643 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3644 * the element (defaults to this.defaultAlign)
3645 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3647 show : function(el, pos, parentMenu)
3649 if (false === this.fireEvent("beforeshow", this)) {
3650 Roo.log("show canceled");
3653 this.parentMenu = parentMenu;
3658 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3661 * Displays this menu at a specific xy position
3662 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3663 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3665 showAt : function(xy, parentMenu, /* private: */_e){
3666 this.parentMenu = parentMenu;
3671 this.fireEvent("beforeshow", this);
3672 //xy = this.el.adjustForConstraints(xy);
3676 this.hideMenuItems();
3677 this.hidden = false;
3678 this.triggerEl.addClass('open');
3679 this.el.addClass('show');
3681 // reassign x when hitting right
3682 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3683 xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3686 // reassign y when hitting bottom
3687 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3688 xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3691 // but the list may align on trigger left or trigger top... should it be a properity?
3693 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3698 this.fireEvent("show", this);
3704 this.doFocus.defer(50, this);
3708 doFocus : function(){
3710 this.focusEl.focus();
3715 * Hides this menu and optionally all parent menus
3716 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3718 hide : function(deep)
3720 if (false === this.fireEvent("beforehide", this)) {
3721 Roo.log("hide canceled");
3724 this.hideMenuItems();
3725 if(this.el && this.isVisible()){
3727 if(this.activeItem){
3728 this.activeItem.deactivate();
3729 this.activeItem = null;
3731 this.triggerEl.removeClass('open');;
3732 this.el.removeClass('show');
3734 this.fireEvent("hide", this);
3736 if(deep === true && this.parentMenu){
3737 this.parentMenu.hide(true);
3741 onTriggerClick : function(e)
3743 Roo.log('trigger click');
3745 var target = e.getTarget();
3747 Roo.log(target.nodeName.toLowerCase());
3749 if(target.nodeName.toLowerCase() === 'i'){
3755 onTriggerPress : function(e)
3757 Roo.log('trigger press');
3758 //Roo.log(e.getTarget());
3759 // Roo.log(this.triggerEl.dom);
3761 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3762 var pel = Roo.get(e.getTarget());
3763 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3764 Roo.log('is treeview or dropdown?');
3768 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3772 if (this.isVisible()) {
3777 this.show(this.triggerEl, '?', false);
3780 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3787 hideMenuItems : function()
3789 Roo.log("hide Menu Items");
3794 this.el.select('.open',true).each(function(aa) {
3796 aa.removeClass('open');
3800 addxtypeChild : function (tree, cntr) {
3801 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3803 this.menuitems.add(comp);
3815 this.getEl().dom.innerHTML = '';
3816 this.menuitems.clear();
3830 * @class Roo.bootstrap.MenuItem
3831 * @extends Roo.bootstrap.Component
3832 * Bootstrap MenuItem class
3833 * @cfg {String} html the menu label
3834 * @cfg {String} href the link
3835 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3836 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3837 * @cfg {Boolean} active used on sidebars to highlight active itesm
3838 * @cfg {String} fa favicon to show on left of menu item.
3839 * @cfg {Roo.bootsrap.Menu} menu the child menu.
3843 * Create a new MenuItem
3844 * @param {Object} config The config object
3848 Roo.bootstrap.MenuItem = function(config){
3849 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3854 * The raw click event for the entire grid.
3855 * @param {Roo.bootstrap.MenuItem} this
3856 * @param {Roo.EventObject} e
3862 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
3866 preventDefault: false,
3867 isContainer : false,
3871 getAutoCreate : function(){
3873 if(this.isContainer){
3876 cls: 'dropdown-menu-item '
3886 cls : 'dropdown-item',
3891 if (this.fa !== false) {
3894 cls : 'fa fa-' + this.fa
3903 cls: 'dropdown-menu-item',
3906 if (this.parent().type == 'treeview') {
3907 cfg.cls = 'treeview-menu';
3910 cfg.cls += ' active';
3915 anc.href = this.href || cfg.cn[0].href ;
3916 ctag.html = this.html || cfg.cn[0].html ;
3920 initEvents: function()
3922 if (this.parent().type == 'treeview') {
3923 this.el.select('a').on('click', this.onClick, this);
3927 this.menu.parentType = this.xtype;
3928 this.menu.triggerEl = this.el;
3929 this.menu = this.addxtype(Roo.apply({}, this.menu));
3933 onClick : function(e)
3935 Roo.log('item on click ');
3937 if(this.preventDefault){
3940 //this.parent().hideMenuItems();
3942 this.fireEvent('click', this, e);
3961 * @class Roo.bootstrap.MenuSeparator
3962 * @extends Roo.bootstrap.Component
3963 * Bootstrap MenuSeparator class
3966 * Create a new MenuItem
3967 * @param {Object} config The config object
3971 Roo.bootstrap.MenuSeparator = function(config){
3972 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3975 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
3977 getAutoCreate : function(){
3996 * @class Roo.bootstrap.Modal
3997 * @extends Roo.bootstrap.Component
3998 * Bootstrap Modal class
3999 * @cfg {String} title Title of dialog
4000 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4001 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4002 * @cfg {Boolean} specificTitle default false
4003 * @cfg {Array} buttons Array of buttons or standard button set..
4004 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4005 * @cfg {Boolean} animate default true
4006 * @cfg {Boolean} allow_close default true
4007 * @cfg {Boolean} fitwindow default false
4008 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4009 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4010 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4011 * @cfg {String} size (sm|lg|xl) default empty
4012 * @cfg {Number} max_width set the max width of modal
4013 * @cfg {Boolean} editableTitle can the title be edited
4018 * Create a new Modal Dialog
4019 * @param {Object} config The config object
4022 Roo.bootstrap.Modal = function(config){
4023 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4028 * The raw btnclick event for the button
4029 * @param {Roo.EventObject} e
4034 * Fire when dialog resize
4035 * @param {Roo.bootstrap.Modal} this
4036 * @param {Roo.EventObject} e
4040 * @event titlechanged
4041 * Fire when the editable title has been changed
4042 * @param {Roo.bootstrap.Modal} this
4043 * @param {Roo.EventObject} value
4045 "titlechanged" : true
4048 this.buttons = this.buttons || [];
4051 this.tmpl = Roo.factory(this.tmpl);
4056 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4058 title : 'test dialog',
4068 specificTitle: false,
4070 buttonPosition: 'right',
4092 editableTitle : false,
4094 onRender : function(ct, position)
4096 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4099 var cfg = Roo.apply({}, this.getAutoCreate());
4102 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4104 //if (!cfg.name.length) {
4108 cfg.cls += ' ' + this.cls;
4111 cfg.style = this.style;
4113 this.el = Roo.get(document.body).createChild(cfg, position);
4115 //var type = this.el.dom.type;
4118 if(this.tabIndex !== undefined){
4119 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4122 this.dialogEl = this.el.select('.modal-dialog',true).first();
4123 this.bodyEl = this.el.select('.modal-body',true).first();
4124 this.closeEl = this.el.select('.modal-header .close', true).first();
4125 this.headerEl = this.el.select('.modal-header',true).first();
4126 this.titleEl = this.el.select('.modal-title',true).first();
4127 this.footerEl = this.el.select('.modal-footer',true).first();
4129 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4131 //this.el.addClass("x-dlg-modal");
4133 if (this.buttons.length) {
4134 Roo.each(this.buttons, function(bb) {
4135 var b = Roo.apply({}, bb);
4136 b.xns = b.xns || Roo.bootstrap;
4137 b.xtype = b.xtype || 'Button';
4138 if (typeof(b.listeners) == 'undefined') {
4139 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4142 var btn = Roo.factory(b);
4144 btn.render(this.getButtonContainer());
4148 // render the children.
4151 if(typeof(this.items) != 'undefined'){
4152 var items = this.items;
4155 for(var i =0;i < items.length;i++) {
4156 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4160 this.items = nitems;
4162 // where are these used - they used to be body/close/footer
4166 //this.el.addClass([this.fieldClass, this.cls]);
4170 getAutoCreate : function()
4172 // we will default to modal-body-overflow - might need to remove or make optional later.
4174 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4175 html : this.html || ''
4180 cls : 'modal-title',
4184 if(this.specificTitle){ // WTF is this?
4189 if (this.allow_close && Roo.bootstrap.version == 3) {
4199 if (this.editableTitle) {
4201 cls: 'form-control roo-editable-title d-none',
4207 if (this.allow_close && Roo.bootstrap.version == 4) {
4217 if(this.size.length){
4218 size = 'modal-' + this.size;
4221 var footer = Roo.bootstrap.version == 3 ?
4223 cls : 'modal-footer',
4227 cls: 'btn-' + this.buttonPosition
4232 { // BS4 uses mr-auto on left buttons....
4233 cls : 'modal-footer'
4244 cls: "modal-dialog " + size,
4247 cls : "modal-content",
4250 cls : 'modal-header',
4265 modal.cls += ' fade';
4271 getChildContainer : function() {
4276 getButtonContainer : function() {
4278 return Roo.bootstrap.version == 4 ?
4279 this.el.select('.modal-footer',true).first()
4280 : this.el.select('.modal-footer div',true).first();
4283 initEvents : function()
4285 if (this.allow_close) {
4286 this.closeEl.on('click', this.hide, this);
4288 Roo.EventManager.onWindowResize(this.resize, this, true);
4289 if (this.editableTitle) {
4290 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4291 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4292 this.headerEditEl.on('keyup', function(e) {
4293 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4294 this.toggleHeaderInput(false)
4297 this.headerEditEl.on('blur', function(e) {
4298 this.toggleHeaderInput(false)
4307 this.maskEl.setSize(
4308 Roo.lib.Dom.getViewWidth(true),
4309 Roo.lib.Dom.getViewHeight(true)
4312 if (this.fitwindow) {
4314 this.dialogEl.setStyle( { 'max-width' : '100%' });
4316 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4317 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4322 if(this.max_width !== 0) {
4324 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4327 this.setSize(w, this.height);
4331 if(this.max_height) {
4332 this.setSize(w,Math.min(
4334 Roo.lib.Dom.getViewportHeight(true) - 60
4340 if(!this.fit_content) {
4341 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4345 this.setSize(w, Math.min(
4347 this.headerEl.getHeight() +
4348 this.footerEl.getHeight() +
4349 this.getChildHeight(this.bodyEl.dom.childNodes),
4350 Roo.lib.Dom.getViewportHeight(true) - 60)
4356 setSize : function(w,h)
4367 if (!this.rendered) {
4370 this.toggleHeaderInput(false);
4371 //this.el.setStyle('display', 'block');
4372 this.el.removeClass('hideing');
4373 this.el.dom.style.display='block';
4375 Roo.get(document.body).addClass('modal-open');
4377 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4380 this.el.addClass('show');
4381 this.el.addClass('in');
4384 this.el.addClass('show');
4385 this.el.addClass('in');
4388 // not sure how we can show data in here..
4390 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4393 Roo.get(document.body).addClass("x-body-masked");
4395 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4396 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4397 this.maskEl.dom.style.display = 'block';
4398 this.maskEl.addClass('show');
4403 this.fireEvent('show', this);
4405 // set zindex here - otherwise it appears to be ignored...
4406 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4409 this.items.forEach( function(e) {
4410 e.layout ? e.layout() : false;
4418 if(this.fireEvent("beforehide", this) !== false){
4420 this.maskEl.removeClass('show');
4422 this.maskEl.dom.style.display = '';
4423 Roo.get(document.body).removeClass("x-body-masked");
4424 this.el.removeClass('in');
4425 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4427 if(this.animate){ // why
4428 this.el.addClass('hideing');
4429 this.el.removeClass('show');
4431 if (!this.el.hasClass('hideing')) {
4432 return; // it's been shown again...
4435 this.el.dom.style.display='';
4437 Roo.get(document.body).removeClass('modal-open');
4438 this.el.removeClass('hideing');
4442 this.el.removeClass('show');
4443 this.el.dom.style.display='';
4444 Roo.get(document.body).removeClass('modal-open');
4447 this.fireEvent('hide', this);
4450 isVisible : function()
4453 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4457 addButton : function(str, cb)
4461 var b = Roo.apply({}, { html : str } );
4462 b.xns = b.xns || Roo.bootstrap;
4463 b.xtype = b.xtype || 'Button';
4464 if (typeof(b.listeners) == 'undefined') {
4465 b.listeners = { click : cb.createDelegate(this) };
4468 var btn = Roo.factory(b);
4470 btn.render(this.getButtonContainer());
4476 setDefaultButton : function(btn)
4478 //this.el.select('.modal-footer').()
4481 resizeTo: function(w,h)
4483 this.dialogEl.setWidth(w);
4485 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4487 this.bodyEl.setHeight(h - diff);
4489 this.fireEvent('resize', this);
4492 setContentSize : function(w, h)
4496 onButtonClick: function(btn,e)
4499 this.fireEvent('btnclick', btn.name, e);
4502 * Set the title of the Dialog
4503 * @param {String} str new Title
4505 setTitle: function(str) {
4506 this.titleEl.dom.innerHTML = str;
4510 * Set the body of the Dialog
4511 * @param {String} str new Title
4513 setBody: function(str) {
4514 this.bodyEl.dom.innerHTML = str;
4517 * Set the body of the Dialog using the template
4518 * @param {Obj} data - apply this data to the template and replace the body contents.
4520 applyBody: function(obj)
4523 Roo.log("Error - using apply Body without a template");
4526 this.tmpl.overwrite(this.bodyEl, obj);
4529 getChildHeight : function(child_nodes)
4533 child_nodes.length == 0
4538 var child_height = 0;
4540 for(var i = 0; i < child_nodes.length; i++) {
4543 * for modal with tabs...
4544 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4546 var layout_childs = child_nodes[i].childNodes;
4548 for(var j = 0; j < layout_childs.length; j++) {
4550 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4552 var layout_body_childs = layout_childs[j].childNodes;
4554 for(var k = 0; k < layout_body_childs.length; k++) {
4556 if(layout_body_childs[k].classList.contains('navbar')) {
4557 child_height += layout_body_childs[k].offsetHeight;
4561 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4563 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4565 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4567 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4568 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4583 child_height += child_nodes[i].offsetHeight;
4584 // Roo.log(child_nodes[i].offsetHeight);
4587 return child_height;
4589 toggleHeaderInput : function(is_edit)
4591 if (!this.editableTitle) {
4592 return; // not editable.
4594 if (is_edit && this.is_header_editing) {
4595 return; // already editing..
4599 this.headerEditEl.dom.value = this.title;
4600 this.headerEditEl.removeClass('d-none');
4601 this.headerEditEl.dom.focus();
4602 this.titleEl.addClass('d-none');
4604 this.is_header_editing = true;
4607 // flip back to not editing.
4608 this.title = this.headerEditEl.dom.value;
4609 this.headerEditEl.addClass('d-none');
4610 this.titleEl.removeClass('d-none');
4611 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4612 this.is_header_editing = false;
4613 this.fireEvent('titlechanged', this, this.title);
4622 Roo.apply(Roo.bootstrap.Modal, {
4624 * Button config that displays a single OK button
4633 * Button config that displays Yes and No buttons
4649 * Button config that displays OK and Cancel buttons
4664 * Button config that displays Yes, No and Cancel buttons
4689 * messagebox - can be used as a replace
4693 * @class Roo.MessageBox
4694 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4698 Roo.Msg.alert('Status', 'Changes saved successfully.');
4700 // Prompt for user data:
4701 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4703 // process text value...
4707 // Show a dialog using config options:
4709 title:'Save Changes?',
4710 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4711 buttons: Roo.Msg.YESNOCANCEL,
4718 Roo.bootstrap.MessageBox = function(){
4719 var dlg, opt, mask, waitTimer;
4720 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4721 var buttons, activeTextEl, bwidth;
4725 var handleButton = function(button){
4727 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4731 var handleHide = function(){
4733 dlg.el.removeClass(opt.cls);
4736 // Roo.TaskMgr.stop(waitTimer);
4737 // waitTimer = null;
4742 var updateButtons = function(b){
4745 buttons["ok"].hide();
4746 buttons["cancel"].hide();
4747 buttons["yes"].hide();
4748 buttons["no"].hide();
4749 dlg.footerEl.hide();
4753 dlg.footerEl.show();
4754 for(var k in buttons){
4755 if(typeof buttons[k] != "function"){
4758 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4759 width += buttons[k].el.getWidth()+15;
4769 var handleEsc = function(d, k, e){
4770 if(opt && opt.closable !== false){
4780 * Returns a reference to the underlying {@link Roo.BasicDialog} element
4781 * @return {Roo.BasicDialog} The BasicDialog element
4783 getDialog : function(){
4785 dlg = new Roo.bootstrap.Modal( {
4788 //constraintoviewport:false,
4790 //collapsible : false,
4795 //buttonAlign:"center",
4796 closeClick : function(){
4797 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4800 handleButton("cancel");
4805 dlg.on("hide", handleHide);
4807 //dlg.addKeyListener(27, handleEsc);
4809 this.buttons = buttons;
4810 var bt = this.buttonText;
4811 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4812 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4813 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4814 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4816 bodyEl = dlg.bodyEl.createChild({
4818 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4819 '<textarea class="roo-mb-textarea"></textarea>' +
4820 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
4822 msgEl = bodyEl.dom.firstChild;
4823 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4824 textboxEl.enableDisplayMode();
4825 textboxEl.addKeyListener([10,13], function(){
4826 if(dlg.isVisible() && opt && opt.buttons){
4829 }else if(opt.buttons.yes){
4830 handleButton("yes");
4834 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4835 textareaEl.enableDisplayMode();
4836 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4837 progressEl.enableDisplayMode();
4839 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4840 var pf = progressEl.dom.firstChild;
4842 pp = Roo.get(pf.firstChild);
4843 pp.setHeight(pf.offsetHeight);
4851 * Updates the message box body text
4852 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4853 * the XHTML-compliant non-breaking space character '&#160;')
4854 * @return {Roo.MessageBox} This message box
4856 updateText : function(text)
4858 if(!dlg.isVisible() && !opt.width){
4859 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4860 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4862 msgEl.innerHTML = text || ' ';
4864 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4865 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4867 Math.min(opt.width || cw , this.maxWidth),
4868 Math.max(opt.minWidth || this.minWidth, bwidth)
4871 activeTextEl.setWidth(w);
4873 if(dlg.isVisible()){
4874 dlg.fixedcenter = false;
4876 // to big, make it scroll. = But as usual stupid IE does not support
4879 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4880 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4881 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4883 bodyEl.dom.style.height = '';
4884 bodyEl.dom.style.overflowY = '';
4887 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4889 bodyEl.dom.style.overflowX = '';
4892 dlg.setContentSize(w, bodyEl.getHeight());
4893 if(dlg.isVisible()){
4894 dlg.fixedcenter = true;
4900 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
4901 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4902 * @param {Number} value Any number between 0 and 1 (e.g., .5)
4903 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4904 * @return {Roo.MessageBox} This message box
4906 updateProgress : function(value, text){
4908 this.updateText(text);
4911 if (pp) { // weird bug on my firefox - for some reason this is not defined
4912 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4913 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4919 * Returns true if the message box is currently displayed
4920 * @return {Boolean} True if the message box is visible, else false
4922 isVisible : function(){
4923 return dlg && dlg.isVisible();
4927 * Hides the message box if it is displayed
4930 if(this.isVisible()){
4936 * Displays a new message box, or reinitializes an existing message box, based on the config options
4937 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4938 * The following config object properties are supported:
4940 Property Type Description
4941 ---------- --------------- ------------------------------------------------------------------------------------
4942 animEl String/Element An id or Element from which the message box should animate as it opens and
4943 closes (defaults to undefined)
4944 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4945 cancel:'Bar'}), or false to not show any buttons (defaults to false)
4946 closable Boolean False to hide the top-right close button (defaults to true). Note that
4947 progress and wait dialogs will ignore this property and always hide the
4948 close button as they can only be closed programmatically.
4949 cls String A custom CSS class to apply to the message box element
4950 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
4951 displayed (defaults to 75)
4952 fn Function A callback function to execute after closing the dialog. The arguments to the
4953 function will be btn (the name of the button that was clicked, if applicable,
4954 e.g. "ok"), and text (the value of the active text field, if applicable).
4955 Progress and wait dialogs will ignore this option since they do not respond to
4956 user actions and can only be closed programmatically, so any required function
4957 should be called by the same code after it closes the dialog.
4958 icon String A CSS class that provides a background image to be used as an icon for
4959 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4960 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
4961 minWidth Number The minimum width in pixels of the message box (defaults to 100)
4962 modal Boolean False to allow user interaction with the page while the message box is
4963 displayed (defaults to true)
4964 msg String A string that will replace the existing message box body text (defaults
4965 to the XHTML-compliant non-breaking space character ' ')
4966 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
4967 progress Boolean True to display a progress bar (defaults to false)
4968 progressText String The text to display inside the progress bar if progress = true (defaults to '')
4969 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
4970 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
4971 title String The title text
4972 value String The string value to set into the active textbox element if displayed
4973 wait Boolean True to display a progress bar (defaults to false)
4974 width Number The width of the dialog in pixels
4981 msg: 'Please enter your address:',
4983 buttons: Roo.MessageBox.OKCANCEL,
4986 animEl: 'addAddressBtn'
4989 * @param {Object} config Configuration options
4990 * @return {Roo.MessageBox} This message box
4992 show : function(options)
4995 // this causes nightmares if you show one dialog after another
4996 // especially on callbacks..
4998 if(this.isVisible()){
5001 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5002 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5003 Roo.log("New Dialog Message:" + options.msg )
5004 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5005 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5008 var d = this.getDialog();
5010 d.setTitle(opt.title || " ");
5011 d.closeEl.setDisplayed(opt.closable !== false);
5012 activeTextEl = textboxEl;
5013 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5018 textareaEl.setHeight(typeof opt.multiline == "number" ?
5019 opt.multiline : this.defaultTextHeight);
5020 activeTextEl = textareaEl;
5029 progressEl.setDisplayed(opt.progress === true);
5031 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5033 this.updateProgress(0);
5034 activeTextEl.dom.value = opt.value || "";
5036 dlg.setDefaultButton(activeTextEl);
5038 var bs = opt.buttons;
5042 }else if(bs && bs.yes){
5043 db = buttons["yes"];
5045 dlg.setDefaultButton(db);
5047 bwidth = updateButtons(opt.buttons);
5048 this.updateText(opt.msg);
5050 d.el.addClass(opt.cls);
5052 d.proxyDrag = opt.proxyDrag === true;
5053 d.modal = opt.modal !== false;
5054 d.mask = opt.modal !== false ? mask : false;
5056 // force it to the end of the z-index stack so it gets a cursor in FF
5057 document.body.appendChild(dlg.el.dom);
5058 d.animateTarget = null;
5059 d.show(options.animEl);
5065 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5066 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5067 * and closing the message box when the process is complete.
5068 * @param {String} title The title bar text
5069 * @param {String} msg The message box body text
5070 * @return {Roo.MessageBox} This message box
5072 progress : function(title, msg){
5079 minWidth: this.minProgressWidth,
5086 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5087 * If a callback function is passed it will be called after the user clicks the button, and the
5088 * id of the button that was clicked will be passed as the only parameter to the callback
5089 * (could also be the top-right close button).
5090 * @param {String} title The title bar text
5091 * @param {String} msg The message box body text
5092 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5093 * @param {Object} scope (optional) The scope of the callback function
5094 * @return {Roo.MessageBox} This message box
5096 alert : function(title, msg, fn, scope)
5111 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5112 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5113 * You are responsible for closing the message box when the process is complete.
5114 * @param {String} msg The message box body text
5115 * @param {String} title (optional) The title bar text
5116 * @return {Roo.MessageBox} This message box
5118 wait : function(msg, title){
5129 waitTimer = Roo.TaskMgr.start({
5131 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5139 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5140 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5141 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5142 * @param {String} title The title bar text
5143 * @param {String} msg The message box body text
5144 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5145 * @param {Object} scope (optional) The scope of the callback function
5146 * @return {Roo.MessageBox} This message box
5148 confirm : function(title, msg, fn, scope){
5152 buttons: this.YESNO,
5161 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5162 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5163 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5164 * (could also be the top-right close button) and the text that was entered will be passed as the two
5165 * parameters to the callback.
5166 * @param {String} title The title bar text
5167 * @param {String} msg The message box body text
5168 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5169 * @param {Object} scope (optional) The scope of the callback function
5170 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5171 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5172 * @return {Roo.MessageBox} This message box
5174 prompt : function(title, msg, fn, scope, multiline){
5178 buttons: this.OKCANCEL,
5183 multiline: multiline,
5190 * Button config that displays a single OK button
5195 * Button config that displays Yes and No buttons
5198 YESNO : {yes:true, no:true},
5200 * Button config that displays OK and Cancel buttons
5203 OKCANCEL : {ok:true, cancel:true},
5205 * Button config that displays Yes, No and Cancel buttons
5208 YESNOCANCEL : {yes:true, no:true, cancel:true},
5211 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5214 defaultTextHeight : 75,
5216 * The maximum width in pixels of the message box (defaults to 600)
5221 * The minimum width in pixels of the message box (defaults to 100)
5226 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5227 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5230 minProgressWidth : 250,
5232 * An object containing the default button text strings that can be overriden for localized language support.
5233 * Supported properties are: ok, cancel, yes and no.
5234 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5247 * Shorthand for {@link Roo.MessageBox}
5249 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5250 Roo.Msg = Roo.Msg || Roo.MessageBox;
5259 * @class Roo.bootstrap.Navbar
5260 * @extends Roo.bootstrap.Component
5261 * Bootstrap Navbar class
5264 * Create a new Navbar
5265 * @param {Object} config The config object
5269 Roo.bootstrap.Navbar = function(config){
5270 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5274 * @event beforetoggle
5275 * Fire before toggle the menu
5276 * @param {Roo.EventObject} e
5278 "beforetoggle" : true
5282 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5291 getAutoCreate : function(){
5294 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5298 initEvents :function ()
5300 //Roo.log(this.el.select('.navbar-toggle',true));
5301 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5308 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5310 var size = this.el.getSize();
5311 this.maskEl.setSize(size.width, size.height);
5312 this.maskEl.enableDisplayMode("block");
5321 getChildContainer : function()
5323 if (this.el && this.el.select('.collapse').getCount()) {
5324 return this.el.select('.collapse',true).first();
5339 onToggle : function()
5342 if(this.fireEvent('beforetoggle', this) === false){
5345 var ce = this.el.select('.navbar-collapse',true).first();
5347 if (!ce.hasClass('show')) {
5357 * Expand the navbar pulldown
5359 expand : function ()
5362 var ce = this.el.select('.navbar-collapse',true).first();
5363 if (ce.hasClass('collapsing')) {
5366 ce.dom.style.height = '';
5368 ce.addClass('in'); // old...
5369 ce.removeClass('collapse');
5370 ce.addClass('show');
5371 var h = ce.getHeight();
5373 ce.removeClass('show');
5374 // at this point we should be able to see it..
5375 ce.addClass('collapsing');
5377 ce.setHeight(0); // resize it ...
5378 ce.on('transitionend', function() {
5379 //Roo.log('done transition');
5380 ce.removeClass('collapsing');
5381 ce.addClass('show');
5382 ce.removeClass('collapse');
5384 ce.dom.style.height = '';
5385 }, this, { single: true} );
5387 ce.dom.scrollTop = 0;
5390 * Collapse the navbar pulldown
5392 collapse : function()
5394 var ce = this.el.select('.navbar-collapse',true).first();
5396 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5397 // it's collapsed or collapsing..
5400 ce.removeClass('in'); // old...
5401 ce.setHeight(ce.getHeight());
5402 ce.removeClass('show');
5403 ce.addClass('collapsing');
5405 ce.on('transitionend', function() {
5406 ce.dom.style.height = '';
5407 ce.removeClass('collapsing');
5408 ce.addClass('collapse');
5409 }, this, { single: true} );
5429 * @class Roo.bootstrap.NavSimplebar
5430 * @extends Roo.bootstrap.Navbar
5431 * Bootstrap Sidebar class
5433 * @cfg {Boolean} inverse is inverted color
5435 * @cfg {String} type (nav | pills | tabs)
5436 * @cfg {Boolean} arrangement stacked | justified
5437 * @cfg {String} align (left | right) alignment
5439 * @cfg {Boolean} main (true|false) main nav bar? default false
5440 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5442 * @cfg {String} tag (header|footer|nav|div) default is nav
5444 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5448 * Create a new Sidebar
5449 * @param {Object} config The config object
5453 Roo.bootstrap.NavSimplebar = function(config){
5454 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5457 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5473 getAutoCreate : function(){
5477 tag : this.tag || 'div',
5478 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5480 if (['light','white'].indexOf(this.weight) > -1) {
5481 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5483 cfg.cls += ' bg-' + this.weight;
5486 cfg.cls += ' navbar-inverse';
5490 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5492 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5501 cls: 'nav nav-' + this.xtype,
5507 this.type = this.type || 'nav';
5508 if (['tabs','pills'].indexOf(this.type) != -1) {
5509 cfg.cn[0].cls += ' nav-' + this.type
5513 if (this.type!=='nav') {
5514 Roo.log('nav type must be nav/tabs/pills')
5516 cfg.cn[0].cls += ' navbar-nav'
5522 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5523 cfg.cn[0].cls += ' nav-' + this.arrangement;
5527 if (this.align === 'right') {
5528 cfg.cn[0].cls += ' navbar-right';
5553 * navbar-expand-md fixed-top
5557 * @class Roo.bootstrap.NavHeaderbar
5558 * @extends Roo.bootstrap.NavSimplebar
5559 * Bootstrap Sidebar class
5561 * @cfg {String} brand what is brand
5562 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5563 * @cfg {String} brand_href href of the brand
5564 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5565 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5566 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5567 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5570 * Create a new Sidebar
5571 * @param {Object} config The config object
5575 Roo.bootstrap.NavHeaderbar = function(config){
5576 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5580 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5587 desktopCenter : false,
5590 getAutoCreate : function(){
5593 tag: this.nav || 'nav',
5594 cls: 'navbar navbar-expand-md',
5600 if (this.desktopCenter) {
5601 cn.push({cls : 'container', cn : []});
5609 cls: 'navbar-toggle navbar-toggler',
5610 'data-toggle': 'collapse',
5615 html: 'Toggle navigation'
5619 cls: 'icon-bar navbar-toggler-icon'
5632 cn.push( Roo.bootstrap.version == 4 ? btn : {
5634 cls: 'navbar-header',
5643 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5647 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5649 if (['light','white'].indexOf(this.weight) > -1) {
5650 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5652 cfg.cls += ' bg-' + this.weight;
5655 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5656 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5658 // tag can override this..
5660 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5663 if (this.brand !== '') {
5664 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5665 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5667 href: this.brand_href ? this.brand_href : '#',
5668 cls: 'navbar-brand',
5676 cfg.cls += ' main-nav';
5684 getHeaderChildContainer : function()
5686 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5687 return this.el.select('.navbar-header',true).first();
5690 return this.getChildContainer();
5693 getChildContainer : function()
5696 return this.el.select('.roo-navbar-collapse',true).first();
5701 initEvents : function()
5703 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5705 if (this.autohide) {
5710 Roo.get(document).on('scroll',function(e) {
5711 var ns = Roo.get(document).getScroll().top;
5712 var os = prevScroll;
5716 ft.removeClass('slideDown');
5717 ft.addClass('slideUp');
5720 ft.removeClass('slideUp');
5721 ft.addClass('slideDown');
5742 * @class Roo.bootstrap.NavSidebar
5743 * @extends Roo.bootstrap.Navbar
5744 * Bootstrap Sidebar class
5747 * Create a new Sidebar
5748 * @param {Object} config The config object
5752 Roo.bootstrap.NavSidebar = function(config){
5753 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5756 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
5758 sidebar : true, // used by Navbar Item and NavbarGroup at present...
5760 getAutoCreate : function(){
5765 cls: 'sidebar sidebar-nav'
5787 * @class Roo.bootstrap.NavGroup
5788 * @extends Roo.bootstrap.Component
5789 * Bootstrap NavGroup class
5790 * @cfg {String} align (left|right)
5791 * @cfg {Boolean} inverse
5792 * @cfg {String} type (nav|pills|tab) default nav
5793 * @cfg {String} navId - reference Id for navbar.
5794 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5797 * Create a new nav group
5798 * @param {Object} config The config object
5801 Roo.bootstrap.NavGroup = function(config){
5802 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5805 Roo.bootstrap.NavGroup.register(this);
5809 * Fires when the active item changes
5810 * @param {Roo.bootstrap.NavGroup} this
5811 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5812 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
5819 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
5831 getAutoCreate : function()
5833 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5839 if (Roo.bootstrap.version == 4) {
5840 if (['tabs','pills'].indexOf(this.type) != -1) {
5841 cfg.cls += ' nav-' + this.type;
5843 // trying to remove so header bar can right align top?
5844 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5845 // do not use on header bar...
5846 cfg.cls += ' navbar-nav';
5851 if (['tabs','pills'].indexOf(this.type) != -1) {
5852 cfg.cls += ' nav-' + this.type
5854 if (this.type !== 'nav') {
5855 Roo.log('nav type must be nav/tabs/pills')
5857 cfg.cls += ' navbar-nav'
5861 if (this.parent() && this.parent().sidebar) {
5864 cls: 'dashboard-menu sidebar-menu'
5870 if (this.form === true) {
5873 cls: 'navbar-form form-inline'
5875 //nav navbar-right ml-md-auto
5876 if (this.align === 'right') {
5877 cfg.cls += ' navbar-right ml-md-auto';
5879 cfg.cls += ' navbar-left';
5883 if (this.align === 'right') {
5884 cfg.cls += ' navbar-right ml-md-auto';
5886 cfg.cls += ' mr-auto';
5890 cfg.cls += ' navbar-inverse';
5898 * sets the active Navigation item
5899 * @param {Roo.bootstrap.NavItem} the new current navitem
5901 setActiveItem : function(item)
5904 Roo.each(this.navItems, function(v){
5909 v.setActive(false, true);
5916 item.setActive(true, true);
5917 this.fireEvent('changed', this, item, prev);
5922 * gets the active Navigation item
5923 * @return {Roo.bootstrap.NavItem} the current navitem
5925 getActive : function()
5929 Roo.each(this.navItems, function(v){
5940 indexOfNav : function()
5944 Roo.each(this.navItems, function(v,i){
5955 * adds a Navigation item
5956 * @param {Roo.bootstrap.NavItem} the navitem to add
5958 addItem : function(cfg)
5960 if (this.form && Roo.bootstrap.version == 4) {
5963 var cn = new Roo.bootstrap.NavItem(cfg);
5965 cn.parentId = this.id;
5966 cn.onRender(this.el, null);
5970 * register a Navigation item
5971 * @param {Roo.bootstrap.NavItem} the navitem to add
5973 register : function(item)
5975 this.navItems.push( item);
5976 item.navId = this.navId;
5981 * clear all the Navigation item
5984 clearAll : function()
5987 this.el.dom.innerHTML = '';
5990 getNavItem: function(tabId)
5993 Roo.each(this.navItems, function(e) {
5994 if (e.tabId == tabId) {
6004 setActiveNext : function()
6006 var i = this.indexOfNav(this.getActive());
6007 if (i > this.navItems.length) {
6010 this.setActiveItem(this.navItems[i+1]);
6012 setActivePrev : function()
6014 var i = this.indexOfNav(this.getActive());
6018 this.setActiveItem(this.navItems[i-1]);
6020 clearWasActive : function(except) {
6021 Roo.each(this.navItems, function(e) {
6022 if (e.tabId != except.tabId && e.was_active) {
6023 e.was_active = false;
6030 getWasActive : function ()
6033 Roo.each(this.navItems, function(e) {
6048 Roo.apply(Roo.bootstrap.NavGroup, {
6052 * register a Navigation Group
6053 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6055 register : function(navgrp)
6057 this.groups[navgrp.navId] = navgrp;
6061 * fetch a Navigation Group based on the navigation ID
6062 * @param {string} the navgroup to add
6063 * @returns {Roo.bootstrap.NavGroup} the navgroup
6065 get: function(navId) {
6066 if (typeof(this.groups[navId]) == 'undefined') {
6068 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6070 return this.groups[navId] ;
6085 * @class Roo.bootstrap.NavItem
6086 * @extends Roo.bootstrap.Component
6087 * Bootstrap Navbar.NavItem class
6088 * @cfg {String} href link to
6089 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6090 * @cfg {Boolean} button_outline show and outlined button
6091 * @cfg {String} html content of button
6092 * @cfg {String} badge text inside badge
6093 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6094 * @cfg {String} glyphicon DEPRICATED - use fa
6095 * @cfg {String} icon DEPRICATED - use fa
6096 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6097 * @cfg {Boolean} active Is item active
6098 * @cfg {Boolean} disabled Is item disabled
6099 * @cfg {String} linkcls Link Class
6100 * @cfg {Boolean} preventDefault (true | false) default false
6101 * @cfg {String} tabId the tab that this item activates.
6102 * @cfg {String} tagtype (a|span) render as a href or span?
6103 * @cfg {Boolean} animateRef (true|false) link to element default false
6106 * Create a new Navbar Item
6107 * @param {Object} config The config object
6109 Roo.bootstrap.NavItem = function(config){
6110 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6115 * The raw click event for the entire grid.
6116 * @param {Roo.EventObject} e
6121 * Fires when the active item active state changes
6122 * @param {Roo.bootstrap.NavItem} this
6123 * @param {boolean} state the new state
6129 * Fires when scroll to element
6130 * @param {Roo.bootstrap.NavItem} this
6131 * @param {Object} options
6132 * @param {Roo.EventObject} e
6140 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6149 preventDefault : false,
6157 button_outline : false,
6161 getAutoCreate : function(){
6168 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6171 cfg.cls += ' active' ;
6173 if (this.disabled) {
6174 cfg.cls += ' disabled';
6178 if (this.button_weight.length) {
6179 cfg.tag = this.href ? 'a' : 'button';
6180 cfg.html = this.html || '';
6181 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6183 cfg.href = this.href;
6186 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6189 // menu .. should add dropdown-menu class - so no need for carat..
6191 if (this.badge !== '') {
6193 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6198 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6202 href : this.href || "#",
6203 html: this.html || ''
6206 if (this.tagtype == 'a') {
6207 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6211 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6214 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6216 if(this.glyphicon) {
6217 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6222 cfg.cn[0].html += " <span class='caret'></span>";
6226 if (this.badge !== '') {
6228 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6236 onRender : function(ct, position)
6238 // Roo.log("Call onRender: " + this.xtype);
6239 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6243 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6244 this.navLink = this.el.select('.nav-link',true).first();
6249 initEvents: function()
6251 if (typeof (this.menu) != 'undefined') {
6252 this.menu.parentType = this.xtype;
6253 this.menu.triggerEl = this.el;
6254 this.menu = this.addxtype(Roo.apply({}, this.menu));
6257 this.el.on('click', this.onClick, this);
6259 //if(this.tagtype == 'span'){
6260 // this.el.select('span',true).on('click', this.onClick, this);
6263 // at this point parent should be available..
6264 this.parent().register(this);
6267 onClick : function(e)
6269 if (e.getTarget('.dropdown-menu-item')) {
6270 // did you click on a menu itemm.... - then don't trigger onclick..
6275 this.preventDefault ||
6278 Roo.log("NavItem - prevent Default?");
6282 if (this.disabled) {
6286 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6287 if (tg && tg.transition) {
6288 Roo.log("waiting for the transitionend");
6294 //Roo.log("fire event clicked");
6295 if(this.fireEvent('click', this, e) === false){
6299 if(this.tagtype == 'span'){
6303 //Roo.log(this.href);
6304 var ael = this.el.select('a',true).first();
6307 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6308 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6309 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6310 return; // ignore... - it's a 'hash' to another page.
6312 Roo.log("NavItem - prevent Default?");
6314 this.scrollToElement(e);
6318 var p = this.parent();
6320 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6321 if (typeof(p.setActiveItem) !== 'undefined') {
6322 p.setActiveItem(this);
6326 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6327 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6328 // remove the collapsed menu expand...
6329 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6333 isActive: function () {
6336 setActive : function(state, fire, is_was_active)
6338 if (this.active && !state && this.navId) {
6339 this.was_active = true;
6340 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6342 nv.clearWasActive(this);
6346 this.active = state;
6349 this.el.removeClass('active');
6350 this.navLink ? this.navLink.removeClass('active') : false;
6351 } else if (!this.el.hasClass('active')) {
6353 this.el.addClass('active');
6354 if (Roo.bootstrap.version == 4 && this.navLink ) {
6355 this.navLink.addClass('active');
6360 this.fireEvent('changed', this, state);
6363 // show a panel if it's registered and related..
6365 if (!this.navId || !this.tabId || !state || is_was_active) {
6369 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6373 var pan = tg.getPanelByName(this.tabId);
6377 // if we can not flip to new panel - go back to old nav highlight..
6378 if (false == tg.showPanel(pan)) {
6379 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6381 var onav = nv.getWasActive();
6383 onav.setActive(true, false, true);
6392 // this should not be here...
6393 setDisabled : function(state)
6395 this.disabled = state;
6397 this.el.removeClass('disabled');
6398 } else if (!this.el.hasClass('disabled')) {
6399 this.el.addClass('disabled');
6405 * Fetch the element to display the tooltip on.
6406 * @return {Roo.Element} defaults to this.el
6408 tooltipEl : function()
6410 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6413 scrollToElement : function(e)
6415 var c = document.body;
6418 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6420 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6421 c = document.documentElement;
6424 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6430 var o = target.calcOffsetsTo(c);
6437 this.fireEvent('scrollto', this, options, e);
6439 Roo.get(c).scrollTo('top', options.value, true);
6452 * <span> icon </span>
6453 * <span> text </span>
6454 * <span>badge </span>
6458 * @class Roo.bootstrap.NavSidebarItem
6459 * @extends Roo.bootstrap.NavItem
6460 * Bootstrap Navbar.NavSidebarItem class
6461 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6462 * {Boolean} open is the menu open
6463 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6464 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6465 * {String} buttonSize (sm|md|lg)the extra classes for the button
6466 * {Boolean} showArrow show arrow next to the text (default true)
6468 * Create a new Navbar Button
6469 * @param {Object} config The config object
6471 Roo.bootstrap.NavSidebarItem = function(config){
6472 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6477 * The raw click event for the entire grid.
6478 * @param {Roo.EventObject} e
6483 * Fires when the active item active state changes
6484 * @param {Roo.bootstrap.NavSidebarItem} this
6485 * @param {boolean} state the new state
6493 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6495 badgeWeight : 'default',
6501 buttonWeight : 'default',
6507 getAutoCreate : function(){
6512 href : this.href || '#',
6518 if(this.buttonView){
6521 href : this.href || '#',
6522 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6535 cfg.cls += ' active';
6538 if (this.disabled) {
6539 cfg.cls += ' disabled';
6542 cfg.cls += ' open x-open';
6545 if (this.glyphicon || this.icon) {
6546 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6547 a.cn.push({ tag : 'i', cls : c }) ;
6550 if(!this.buttonView){
6553 html : this.html || ''
6560 if (this.badge !== '') {
6561 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6567 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6570 a.cls += ' dropdown-toggle treeview' ;
6576 initEvents : function()
6578 if (typeof (this.menu) != 'undefined') {
6579 this.menu.parentType = this.xtype;
6580 this.menu.triggerEl = this.el;
6581 this.menu = this.addxtype(Roo.apply({}, this.menu));
6584 this.el.on('click', this.onClick, this);
6586 if(this.badge !== ''){
6587 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6592 onClick : function(e)
6599 if(this.preventDefault){
6603 this.fireEvent('click', this, e);
6606 disable : function()
6608 this.setDisabled(true);
6613 this.setDisabled(false);
6616 setDisabled : function(state)
6618 if(this.disabled == state){
6622 this.disabled = state;
6625 this.el.addClass('disabled');
6629 this.el.removeClass('disabled');
6634 setActive : function(state)
6636 if(this.active == state){
6640 this.active = state;
6643 this.el.addClass('active');
6647 this.el.removeClass('active');
6652 isActive: function ()
6657 setBadge : function(str)
6663 this.badgeEl.dom.innerHTML = str;
6678 Roo.namespace('Roo.bootstrap.breadcrumb');
6682 * @class Roo.bootstrap.breadcrumb.Nav
6683 * @extends Roo.bootstrap.Component
6684 * Bootstrap Breadcrumb Nav Class
6686 * @children Roo.bootstrap.breadcrumb.Item
6689 * Create a new breadcrumb.Nav
6690 * @param {Object} config The config object
6694 Roo.bootstrap.breadcrumb.Nav = function(config){
6695 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6700 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6702 getAutoCreate : function()
6719 initEvents: function()
6721 this.olEl = this.el.select('ol',true).first();
6723 getChildContainer : function()
6739 * @class Roo.bootstrap.breadcrumb.Nav
6740 * @extends Roo.bootstrap.Component
6741 * Bootstrap Breadcrumb Nav Class
6743 * @children Roo.bootstrap.breadcrumb.Component
6744 * @cfg {String} html the content of the link.
6745 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6746 * @cfg {Boolean} active is it active
6750 * Create a new breadcrumb.Nav
6751 * @param {Object} config The config object
6754 Roo.bootstrap.breadcrumb.Item = function(config){
6755 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6760 * The img click event for the img.
6761 * @param {Roo.EventObject} e
6768 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
6773 getAutoCreate : function()
6778 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6780 if (this.href !== false) {
6787 cfg.html = this.html;
6793 initEvents: function()
6796 this.el.select('a', true).first().on('click',this.onClick, this)
6800 onClick : function(e)
6803 this.fireEvent('click',this, e);
6816 * @class Roo.bootstrap.Row
6817 * @extends Roo.bootstrap.Component
6818 * Bootstrap Row class (contains columns...)
6822 * @param {Object} config The config object
6825 Roo.bootstrap.Row = function(config){
6826 Roo.bootstrap.Row.superclass.constructor.call(this, config);
6829 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
6831 getAutoCreate : function(){
6850 * @class Roo.bootstrap.Pagination
6851 * @extends Roo.bootstrap.Component
6852 * Bootstrap Pagination class
6853 * @cfg {String} size xs | sm | md | lg
6854 * @cfg {Boolean} inverse false | true
6857 * Create a new Pagination
6858 * @param {Object} config The config object
6861 Roo.bootstrap.Pagination = function(config){
6862 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6865 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
6871 getAutoCreate : function(){
6877 cfg.cls += ' inverse';
6883 cfg.cls += " " + this.cls;
6901 * @class Roo.bootstrap.PaginationItem
6902 * @extends Roo.bootstrap.Component
6903 * Bootstrap PaginationItem class
6904 * @cfg {String} html text
6905 * @cfg {String} href the link
6906 * @cfg {Boolean} preventDefault (true | false) default true
6907 * @cfg {Boolean} active (true | false) default false
6908 * @cfg {Boolean} disabled default false
6912 * Create a new PaginationItem
6913 * @param {Object} config The config object
6917 Roo.bootstrap.PaginationItem = function(config){
6918 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6923 * The raw click event for the entire grid.
6924 * @param {Roo.EventObject} e
6930 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
6934 preventDefault: true,
6939 getAutoCreate : function(){
6945 href : this.href ? this.href : '#',
6946 html : this.html ? this.html : ''
6956 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6960 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6966 initEvents: function() {
6968 this.el.on('click', this.onClick, this);
6971 onClick : function(e)
6973 Roo.log('PaginationItem on click ');
6974 if(this.preventDefault){
6982 this.fireEvent('click', this, e);
6998 * @class Roo.bootstrap.Slider
6999 * @extends Roo.bootstrap.Component
7000 * Bootstrap Slider class
7003 * Create a new Slider
7004 * @param {Object} config The config object
7007 Roo.bootstrap.Slider = function(config){
7008 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7011 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7013 getAutoCreate : function(){
7017 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7021 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7033 * Ext JS Library 1.1.1
7034 * Copyright(c) 2006-2007, Ext JS, LLC.
7036 * Originally Released Under LGPL - original licence link has changed is not relivant.
7039 * <script type="text/javascript">
7044 * @class Roo.grid.ColumnModel
7045 * @extends Roo.util.Observable
7046 * This is the default implementation of a ColumnModel used by the Grid. It defines
7047 * the columns in the grid.
7050 var colModel = new Roo.grid.ColumnModel([
7051 {header: "Ticker", width: 60, sortable: true, locked: true},
7052 {header: "Company Name", width: 150, sortable: true},
7053 {header: "Market Cap.", width: 100, sortable: true},
7054 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7055 {header: "Employees", width: 100, sortable: true, resizable: false}
7060 * The config options listed for this class are options which may appear in each
7061 * individual column definition.
7062 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7064 * @param {Object} config An Array of column config objects. See this class's
7065 * config objects for details.
7067 Roo.grid.ColumnModel = function(config){
7069 * The config passed into the constructor
7071 this.config = config;
7074 // if no id, create one
7075 // if the column does not have a dataIndex mapping,
7076 // map it to the order it is in the config
7077 for(var i = 0, len = config.length; i < len; i++){
7079 if(typeof c.dataIndex == "undefined"){
7082 if(typeof c.renderer == "string"){
7083 c.renderer = Roo.util.Format[c.renderer];
7085 if(typeof c.id == "undefined"){
7088 if(c.editor && c.editor.xtype){
7089 c.editor = Roo.factory(c.editor, Roo.grid);
7091 if(c.editor && c.editor.isFormField){
7092 c.editor = new Roo.grid.GridEditor(c.editor);
7094 this.lookup[c.id] = c;
7098 * The width of columns which have no width specified (defaults to 100)
7101 this.defaultWidth = 100;
7104 * Default sortable of columns which have no sortable specified (defaults to false)
7107 this.defaultSortable = false;
7111 * @event widthchange
7112 * Fires when the width of a column changes.
7113 * @param {ColumnModel} this
7114 * @param {Number} columnIndex The column index
7115 * @param {Number} newWidth The new width
7117 "widthchange": true,
7119 * @event headerchange
7120 * Fires when the text of a header changes.
7121 * @param {ColumnModel} this
7122 * @param {Number} columnIndex The column index
7123 * @param {Number} newText The new header text
7125 "headerchange": true,
7127 * @event hiddenchange
7128 * Fires when a column is hidden or "unhidden".
7129 * @param {ColumnModel} this
7130 * @param {Number} columnIndex The column index
7131 * @param {Boolean} hidden true if hidden, false otherwise
7133 "hiddenchange": true,
7135 * @event columnmoved
7136 * Fires when a column is moved.
7137 * @param {ColumnModel} this
7138 * @param {Number} oldIndex
7139 * @param {Number} newIndex
7141 "columnmoved" : true,
7143 * @event columlockchange
7144 * Fires when a column's locked state is changed
7145 * @param {ColumnModel} this
7146 * @param {Number} colIndex
7147 * @param {Boolean} locked true if locked
7149 "columnlockchange" : true
7151 Roo.grid.ColumnModel.superclass.constructor.call(this);
7153 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7155 * @cfg {String} header The header text to display in the Grid view.
7158 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7159 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7160 * specified, the column's index is used as an index into the Record's data Array.
7163 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7164 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7167 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7168 * Defaults to the value of the {@link #defaultSortable} property.
7169 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7172 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7175 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7178 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7181 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7184 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7185 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7186 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7187 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7190 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7193 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7196 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7199 * @cfg {String} cursor (Optional)
7202 * @cfg {String} tooltip (Optional)
7205 * @cfg {Number} xs (Optional)
7208 * @cfg {Number} sm (Optional)
7211 * @cfg {Number} md (Optional)
7214 * @cfg {Number} lg (Optional)
7217 * Returns the id of the column at the specified index.
7218 * @param {Number} index The column index
7219 * @return {String} the id
7221 getColumnId : function(index){
7222 return this.config[index].id;
7226 * Returns the column for a specified id.
7227 * @param {String} id The column id
7228 * @return {Object} the column
7230 getColumnById : function(id){
7231 return this.lookup[id];
7236 * Returns the column for a specified dataIndex.
7237 * @param {String} dataIndex The column dataIndex
7238 * @return {Object|Boolean} the column or false if not found
7240 getColumnByDataIndex: function(dataIndex){
7241 var index = this.findColumnIndex(dataIndex);
7242 return index > -1 ? this.config[index] : false;
7246 * Returns the index for a specified column id.
7247 * @param {String} id The column id
7248 * @return {Number} the index, or -1 if not found
7250 getIndexById : function(id){
7251 for(var i = 0, len = this.config.length; i < len; i++){
7252 if(this.config[i].id == id){
7260 * Returns the index for a specified column dataIndex.
7261 * @param {String} dataIndex The column dataIndex
7262 * @return {Number} the index, or -1 if not found
7265 findColumnIndex : function(dataIndex){
7266 for(var i = 0, len = this.config.length; i < len; i++){
7267 if(this.config[i].dataIndex == dataIndex){
7275 moveColumn : function(oldIndex, newIndex){
7276 var c = this.config[oldIndex];
7277 this.config.splice(oldIndex, 1);
7278 this.config.splice(newIndex, 0, c);
7279 this.dataMap = null;
7280 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7283 isLocked : function(colIndex){
7284 return this.config[colIndex].locked === true;
7287 setLocked : function(colIndex, value, suppressEvent){
7288 if(this.isLocked(colIndex) == value){
7291 this.config[colIndex].locked = value;
7293 this.fireEvent("columnlockchange", this, colIndex, value);
7297 getTotalLockedWidth : function(){
7299 for(var i = 0; i < this.config.length; i++){
7300 if(this.isLocked(i) && !this.isHidden(i)){
7301 this.totalWidth += this.getColumnWidth(i);
7307 getLockedCount : function(){
7308 for(var i = 0, len = this.config.length; i < len; i++){
7309 if(!this.isLocked(i)){
7314 return this.config.length;
7318 * Returns the number of columns.
7321 getColumnCount : function(visibleOnly){
7322 if(visibleOnly === true){
7324 for(var i = 0, len = this.config.length; i < len; i++){
7325 if(!this.isHidden(i)){
7331 return this.config.length;
7335 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7336 * @param {Function} fn
7337 * @param {Object} scope (optional)
7338 * @return {Array} result
7340 getColumnsBy : function(fn, scope){
7342 for(var i = 0, len = this.config.length; i < len; i++){
7343 var c = this.config[i];
7344 if(fn.call(scope||this, c, i) === true){
7352 * Returns true if the specified column is sortable.
7353 * @param {Number} col The column index
7356 isSortable : function(col){
7357 if(typeof this.config[col].sortable == "undefined"){
7358 return this.defaultSortable;
7360 return this.config[col].sortable;
7364 * Returns the rendering (formatting) function defined for the column.
7365 * @param {Number} col The column index.
7366 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7368 getRenderer : function(col){
7369 if(!this.config[col].renderer){
7370 return Roo.grid.ColumnModel.defaultRenderer;
7372 return this.config[col].renderer;
7376 * Sets the rendering (formatting) function for a column.
7377 * @param {Number} col The column index
7378 * @param {Function} fn The function to use to process the cell's raw data
7379 * to return HTML markup for the grid view. The render function is called with
7380 * the following parameters:<ul>
7381 * <li>Data value.</li>
7382 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7383 * <li>css A CSS style string to apply to the table cell.</li>
7384 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7385 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7386 * <li>Row index</li>
7387 * <li>Column index</li>
7388 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7390 setRenderer : function(col, fn){
7391 this.config[col].renderer = fn;
7395 * Returns the width for the specified column.
7396 * @param {Number} col The column index
7399 getColumnWidth : function(col){
7400 return this.config[col].width * 1 || this.defaultWidth;
7404 * Sets the width for a column.
7405 * @param {Number} col The column index
7406 * @param {Number} width The new width
7408 setColumnWidth : function(col, width, suppressEvent){
7409 this.config[col].width = width;
7410 this.totalWidth = null;
7412 this.fireEvent("widthchange", this, col, width);
7417 * Returns the total width of all columns.
7418 * @param {Boolean} includeHidden True to include hidden column widths
7421 getTotalWidth : function(includeHidden){
7422 if(!this.totalWidth){
7423 this.totalWidth = 0;
7424 for(var i = 0, len = this.config.length; i < len; i++){
7425 if(includeHidden || !this.isHidden(i)){
7426 this.totalWidth += this.getColumnWidth(i);
7430 return this.totalWidth;
7434 * Returns the header for the specified column.
7435 * @param {Number} col The column index
7438 getColumnHeader : function(col){
7439 return this.config[col].header;
7443 * Sets the header for a column.
7444 * @param {Number} col The column index
7445 * @param {String} header The new header
7447 setColumnHeader : function(col, header){
7448 this.config[col].header = header;
7449 this.fireEvent("headerchange", this, col, header);
7453 * Returns the tooltip for the specified column.
7454 * @param {Number} col The column index
7457 getColumnTooltip : function(col){
7458 return this.config[col].tooltip;
7461 * Sets the tooltip for a column.
7462 * @param {Number} col The column index
7463 * @param {String} tooltip The new tooltip
7465 setColumnTooltip : function(col, tooltip){
7466 this.config[col].tooltip = tooltip;
7470 * Returns the dataIndex for the specified column.
7471 * @param {Number} col The column index
7474 getDataIndex : function(col){
7475 return this.config[col].dataIndex;
7479 * Sets the dataIndex for a column.
7480 * @param {Number} col The column index
7481 * @param {Number} dataIndex The new dataIndex
7483 setDataIndex : function(col, dataIndex){
7484 this.config[col].dataIndex = dataIndex;
7490 * Returns true if the cell is editable.
7491 * @param {Number} colIndex The column index
7492 * @param {Number} rowIndex The row index - this is nto actually used..?
7495 isCellEditable : function(colIndex, rowIndex){
7496 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7500 * Returns the editor defined for the cell/column.
7501 * return false or null to disable editing.
7502 * @param {Number} colIndex The column index
7503 * @param {Number} rowIndex The row index
7506 getCellEditor : function(colIndex, rowIndex){
7507 return this.config[colIndex].editor;
7511 * Sets if a column is editable.
7512 * @param {Number} col The column index
7513 * @param {Boolean} editable True if the column is editable
7515 setEditable : function(col, editable){
7516 this.config[col].editable = editable;
7521 * Returns true if the column is hidden.
7522 * @param {Number} colIndex The column index
7525 isHidden : function(colIndex){
7526 return this.config[colIndex].hidden;
7531 * Returns true if the column width cannot be changed
7533 isFixed : function(colIndex){
7534 return this.config[colIndex].fixed;
7538 * Returns true if the column can be resized
7541 isResizable : function(colIndex){
7542 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7545 * Sets if a column is hidden.
7546 * @param {Number} colIndex The column index
7547 * @param {Boolean} hidden True if the column is hidden
7549 setHidden : function(colIndex, hidden){
7550 this.config[colIndex].hidden = hidden;
7551 this.totalWidth = null;
7552 this.fireEvent("hiddenchange", this, colIndex, hidden);
7556 * Sets the editor for a column.
7557 * @param {Number} col The column index
7558 * @param {Object} editor The editor object
7560 setEditor : function(col, editor){
7561 this.config[col].editor = editor;
7565 Roo.grid.ColumnModel.defaultRenderer = function(value)
7567 if(typeof value == "object") {
7570 if(typeof value == "string" && value.length < 1){
7574 return String.format("{0}", value);
7577 // Alias for backwards compatibility
7578 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7581 * Ext JS Library 1.1.1
7582 * Copyright(c) 2006-2007, Ext JS, LLC.
7584 * Originally Released Under LGPL - original licence link has changed is not relivant.
7587 * <script type="text/javascript">
7591 * @class Roo.LoadMask
7592 * A simple utility class for generically masking elements while loading data. If the element being masked has
7593 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7594 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7595 * element's UpdateManager load indicator and will be destroyed after the initial load.
7597 * Create a new LoadMask
7598 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7599 * @param {Object} config The config object
7601 Roo.LoadMask = function(el, config){
7602 this.el = Roo.get(el);
7603 Roo.apply(this, config);
7605 this.store.on('beforeload', this.onBeforeLoad, this);
7606 this.store.on('load', this.onLoad, this);
7607 this.store.on('loadexception', this.onLoadException, this);
7608 this.removeMask = false;
7610 var um = this.el.getUpdateManager();
7611 um.showLoadIndicator = false; // disable the default indicator
7612 um.on('beforeupdate', this.onBeforeLoad, this);
7613 um.on('update', this.onLoad, this);
7614 um.on('failure', this.onLoad, this);
7615 this.removeMask = true;
7619 Roo.LoadMask.prototype = {
7621 * @cfg {Boolean} removeMask
7622 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7623 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7627 * The text to display in a centered loading message box (defaults to 'Loading...')
7631 * @cfg {String} msgCls
7632 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7634 msgCls : 'x-mask-loading',
7637 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7643 * Disables the mask to prevent it from being displayed
7645 disable : function(){
7646 this.disabled = true;
7650 * Enables the mask so that it can be displayed
7652 enable : function(){
7653 this.disabled = false;
7656 onLoadException : function()
7660 if (typeof(arguments[3]) != 'undefined') {
7661 Roo.MessageBox.alert("Error loading",arguments[3]);
7665 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7666 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7673 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7678 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7682 onBeforeLoad : function(){
7684 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7689 destroy : function(){
7691 this.store.un('beforeload', this.onBeforeLoad, this);
7692 this.store.un('load', this.onLoad, this);
7693 this.store.un('loadexception', this.onLoadException, this);
7695 var um = this.el.getUpdateManager();
7696 um.un('beforeupdate', this.onBeforeLoad, this);
7697 um.un('update', this.onLoad, this);
7698 um.un('failure', this.onLoad, this);
7709 * @class Roo.bootstrap.Table
7710 * @extends Roo.bootstrap.Component
7711 * Bootstrap Table class
7712 * @cfg {String} cls table class
7713 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7714 * @cfg {String} bgcolor Specifies the background color for a table
7715 * @cfg {Number} border Specifies whether the table cells should have borders or not
7716 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7717 * @cfg {Number} cellspacing Specifies the space between cells
7718 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7719 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7720 * @cfg {String} sortable Specifies that the table should be sortable
7721 * @cfg {String} summary Specifies a summary of the content of a table
7722 * @cfg {Number} width Specifies the width of a table
7723 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7725 * @cfg {boolean} striped Should the rows be alternative striped
7726 * @cfg {boolean} bordered Add borders to the table
7727 * @cfg {boolean} hover Add hover highlighting
7728 * @cfg {boolean} condensed Format condensed
7729 * @cfg {boolean} responsive Format condensed
7730 * @cfg {Boolean} loadMask (true|false) default false
7731 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7732 * @cfg {Boolean} headerShow (true|false) generate thead, default true
7733 * @cfg {Boolean} rowSelection (true|false) default false
7734 * @cfg {Boolean} cellSelection (true|false) default false
7735 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7736 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
7737 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
7738 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
7742 * Create a new Table
7743 * @param {Object} config The config object
7746 Roo.bootstrap.Table = function(config){
7747 Roo.bootstrap.Table.superclass.constructor.call(this, config);
7752 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7753 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7754 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7755 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7757 this.sm = this.sm || {xtype: 'RowSelectionModel'};
7759 this.sm.grid = this;
7760 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7761 this.sm = this.selModel;
7762 this.sm.xmodule = this.xmodule || false;
7765 if (this.cm && typeof(this.cm.config) == 'undefined') {
7766 this.colModel = new Roo.grid.ColumnModel(this.cm);
7767 this.cm = this.colModel;
7768 this.cm.xmodule = this.xmodule || false;
7771 this.store= Roo.factory(this.store, Roo.data);
7772 this.ds = this.store;
7773 this.ds.xmodule = this.xmodule || false;
7776 if (this.footer && this.store) {
7777 this.footer.dataSource = this.ds;
7778 this.footer = Roo.factory(this.footer);
7785 * Fires when a cell is clicked
7786 * @param {Roo.bootstrap.Table} this
7787 * @param {Roo.Element} el
7788 * @param {Number} rowIndex
7789 * @param {Number} columnIndex
7790 * @param {Roo.EventObject} e
7794 * @event celldblclick
7795 * Fires when a cell is double clicked
7796 * @param {Roo.bootstrap.Table} this
7797 * @param {Roo.Element} el
7798 * @param {Number} rowIndex
7799 * @param {Number} columnIndex
7800 * @param {Roo.EventObject} e
7802 "celldblclick" : true,
7805 * Fires when a row is clicked
7806 * @param {Roo.bootstrap.Table} this
7807 * @param {Roo.Element} el
7808 * @param {Number} rowIndex
7809 * @param {Roo.EventObject} e
7813 * @event rowdblclick
7814 * Fires when a row is double clicked
7815 * @param {Roo.bootstrap.Table} this
7816 * @param {Roo.Element} el
7817 * @param {Number} rowIndex
7818 * @param {Roo.EventObject} e
7820 "rowdblclick" : true,
7823 * Fires when a mouseover occur
7824 * @param {Roo.bootstrap.Table} this
7825 * @param {Roo.Element} el
7826 * @param {Number} rowIndex
7827 * @param {Number} columnIndex
7828 * @param {Roo.EventObject} e
7833 * Fires when a mouseout occur
7834 * @param {Roo.bootstrap.Table} this
7835 * @param {Roo.Element} el
7836 * @param {Number} rowIndex
7837 * @param {Number} columnIndex
7838 * @param {Roo.EventObject} e
7843 * Fires when a row is rendered, so you can change add a style to it.
7844 * @param {Roo.bootstrap.Table} this
7845 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
7849 * @event rowsrendered
7850 * Fires when all the rows have been rendered
7851 * @param {Roo.bootstrap.Table} this
7853 'rowsrendered' : true,
7855 * @event contextmenu
7856 * The raw contextmenu event for the entire grid.
7857 * @param {Roo.EventObject} e
7859 "contextmenu" : true,
7861 * @event rowcontextmenu
7862 * Fires when a row is right clicked
7863 * @param {Roo.bootstrap.Table} this
7864 * @param {Number} rowIndex
7865 * @param {Roo.EventObject} e
7867 "rowcontextmenu" : true,
7869 * @event cellcontextmenu
7870 * Fires when a cell is right clicked
7871 * @param {Roo.bootstrap.Table} this
7872 * @param {Number} rowIndex
7873 * @param {Number} cellIndex
7874 * @param {Roo.EventObject} e
7876 "cellcontextmenu" : true,
7878 * @event headercontextmenu
7879 * Fires when a header is right clicked
7880 * @param {Roo.bootstrap.Table} this
7881 * @param {Number} columnIndex
7882 * @param {Roo.EventObject} e
7884 "headercontextmenu" : true
7888 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
7914 rowSelection : false,
7915 cellSelection : false,
7918 // Roo.Element - the tbody
7920 // Roo.Element - thead element
7923 container: false, // used by gridpanel...
7929 auto_hide_footer : false,
7931 getAutoCreate : function()
7933 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7940 if (this.scrollBody) {
7941 cfg.cls += ' table-body-fixed';
7944 cfg.cls += ' table-striped';
7948 cfg.cls += ' table-hover';
7950 if (this.bordered) {
7951 cfg.cls += ' table-bordered';
7953 if (this.condensed) {
7954 cfg.cls += ' table-condensed';
7956 if (this.responsive) {
7957 cfg.cls += ' table-responsive';
7961 cfg.cls+= ' ' +this.cls;
7964 // this lot should be simplifed...
7977 ].forEach(function(k) {
7985 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7988 if(this.store || this.cm){
7989 if(this.headerShow){
7990 cfg.cn.push(this.renderHeader());
7993 cfg.cn.push(this.renderBody());
7995 if(this.footerShow){
7996 cfg.cn.push(this.renderFooter());
7998 // where does this come from?
7999 //cfg.cls+= ' TableGrid';
8002 return { cn : [ cfg ] };
8005 initEvents : function()
8007 if(!this.store || !this.cm){
8010 if (this.selModel) {
8011 this.selModel.initEvents();
8015 //Roo.log('initEvents with ds!!!!');
8017 this.mainBody = this.el.select('tbody', true).first();
8018 this.mainHead = this.el.select('thead', true).first();
8019 this.mainFoot = this.el.select('tfoot', true).first();
8025 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8026 e.on('click', _this.sort, _this);
8029 this.mainBody.on("click", this.onClick, this);
8030 this.mainBody.on("dblclick", this.onDblClick, this);
8032 // why is this done????? = it breaks dialogs??
8033 //this.parent().el.setStyle('position', 'relative');
8037 this.footer.parentId = this.id;
8038 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8041 this.el.select('tfoot tr td').first().addClass('hide');
8046 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8049 this.store.on('load', this.onLoad, this);
8050 this.store.on('beforeload', this.onBeforeLoad, this);
8051 this.store.on('update', this.onUpdate, this);
8052 this.store.on('add', this.onAdd, this);
8053 this.store.on("clear", this.clear, this);
8055 this.el.on("contextmenu", this.onContextMenu, this);
8057 this.mainBody.on('scroll', this.onBodyScroll, this);
8059 this.cm.on("headerchange", this.onHeaderChange, this);
8061 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8065 onContextMenu : function(e, t)
8067 this.processEvent("contextmenu", e);
8070 processEvent : function(name, e)
8072 if (name != 'touchstart' ) {
8073 this.fireEvent(name, e);
8076 var t = e.getTarget();
8078 var cell = Roo.get(t);
8084 if(cell.findParent('tfoot', false, true)){
8088 if(cell.findParent('thead', false, true)){
8090 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8091 cell = Roo.get(t).findParent('th', false, true);
8093 Roo.log("failed to find th in thead?");
8094 Roo.log(e.getTarget());
8099 var cellIndex = cell.dom.cellIndex;
8101 var ename = name == 'touchstart' ? 'click' : name;
8102 this.fireEvent("header" + ename, this, cellIndex, e);
8107 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8108 cell = Roo.get(t).findParent('td', false, true);
8110 Roo.log("failed to find th in tbody?");
8111 Roo.log(e.getTarget());
8116 var row = cell.findParent('tr', false, true);
8117 var cellIndex = cell.dom.cellIndex;
8118 var rowIndex = row.dom.rowIndex - 1;
8122 this.fireEvent("row" + name, this, rowIndex, e);
8126 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8132 onMouseover : function(e, el)
8134 var cell = Roo.get(el);
8140 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8141 cell = cell.findParent('td', false, true);
8144 var row = cell.findParent('tr', false, true);
8145 var cellIndex = cell.dom.cellIndex;
8146 var rowIndex = row.dom.rowIndex - 1; // start from 0
8148 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8152 onMouseout : function(e, el)
8154 var cell = Roo.get(el);
8160 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8161 cell = cell.findParent('td', false, true);
8164 var row = cell.findParent('tr', false, true);
8165 var cellIndex = cell.dom.cellIndex;
8166 var rowIndex = row.dom.rowIndex - 1; // start from 0
8168 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8172 onClick : function(e, el)
8174 var cell = Roo.get(el);
8176 if(!cell || (!this.cellSelection && !this.rowSelection)){
8180 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8181 cell = cell.findParent('td', false, true);
8184 if(!cell || typeof(cell) == 'undefined'){
8188 var row = cell.findParent('tr', false, true);
8190 if(!row || typeof(row) == 'undefined'){
8194 var cellIndex = cell.dom.cellIndex;
8195 var rowIndex = this.getRowIndex(row);
8197 // why??? - should these not be based on SelectionModel?
8198 if(this.cellSelection){
8199 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8202 if(this.rowSelection){
8203 this.fireEvent('rowclick', this, row, rowIndex, e);
8209 onDblClick : function(e,el)
8211 var cell = Roo.get(el);
8213 if(!cell || (!this.cellSelection && !this.rowSelection)){
8217 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8218 cell = cell.findParent('td', false, true);
8221 if(!cell || typeof(cell) == 'undefined'){
8225 var row = cell.findParent('tr', false, true);
8227 if(!row || typeof(row) == 'undefined'){
8231 var cellIndex = cell.dom.cellIndex;
8232 var rowIndex = this.getRowIndex(row);
8234 if(this.cellSelection){
8235 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8238 if(this.rowSelection){
8239 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8243 sort : function(e,el)
8245 var col = Roo.get(el);
8247 if(!col.hasClass('sortable')){
8251 var sort = col.attr('sort');
8254 if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8258 this.store.sortInfo = {field : sort, direction : dir};
8261 Roo.log("calling footer first");
8262 this.footer.onClick('first');
8265 this.store.load({ params : { start : 0 } });
8269 renderHeader : function()
8277 this.totalWidth = 0;
8279 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8281 var config = cm.config[i];
8285 cls : 'x-hcol-' + i,
8287 html: cm.getColumnHeader(i)
8292 if(typeof(config.sortable) != 'undefined' && config.sortable){
8294 c.html = '<i class="glyphicon"></i>' + c.html;
8297 // could use BS4 hidden-..-down
8299 if(typeof(config.lgHeader) != 'undefined'){
8300 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8303 if(typeof(config.mdHeader) != 'undefined'){
8304 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8307 if(typeof(config.smHeader) != 'undefined'){
8308 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8311 if(typeof(config.xsHeader) != 'undefined'){
8312 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8319 if(typeof(config.tooltip) != 'undefined'){
8320 c.tooltip = config.tooltip;
8323 if(typeof(config.colspan) != 'undefined'){
8324 c.colspan = config.colspan;
8327 if(typeof(config.hidden) != 'undefined' && config.hidden){
8328 c.style += ' display:none;';
8331 if(typeof(config.dataIndex) != 'undefined'){
8332 c.sort = config.dataIndex;
8337 if(typeof(config.align) != 'undefined' && config.align.length){
8338 c.style += ' text-align:' + config.align + ';';
8341 if(typeof(config.width) != 'undefined'){
8342 c.style += ' width:' + config.width + 'px;';
8343 this.totalWidth += config.width;
8345 this.totalWidth += 100; // assume minimum of 100 per column?
8348 if(typeof(config.cls) != 'undefined'){
8349 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8352 ['xs','sm','md','lg'].map(function(size){
8354 if(typeof(config[size]) == 'undefined'){
8358 if (!config[size]) { // 0 = hidden
8359 // BS 4 '0' is treated as hide that column and below.
8360 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8364 c.cls += ' col-' + size + '-' + config[size] + (
8365 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8377 renderBody : function()
8387 colspan : this.cm.getColumnCount()
8397 renderFooter : function()
8407 colspan : this.cm.getColumnCount()
8421 // Roo.log('ds onload');
8426 var ds = this.store;
8428 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8429 e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8430 if (_this.store.sortInfo) {
8432 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8433 e.select('i', true).addClass(['glyphicon-arrow-up']);
8436 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8437 e.select('i', true).addClass(['glyphicon-arrow-down']);
8442 var tbody = this.mainBody;
8444 if(ds.getCount() > 0){
8445 ds.data.each(function(d,rowIndex){
8446 var row = this.renderRow(cm, ds, rowIndex);
8448 tbody.createChild(row);
8452 if(row.cellObjects.length){
8453 Roo.each(row.cellObjects, function(r){
8454 _this.renderCellObject(r);
8461 var tfoot = this.el.select('tfoot', true).first();
8463 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8465 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8467 var total = this.ds.getTotalCount();
8469 if(this.footer.pageSize < total){
8470 this.mainFoot.show();
8474 Roo.each(this.el.select('tbody td', true).elements, function(e){
8475 e.on('mouseover', _this.onMouseover, _this);
8478 Roo.each(this.el.select('tbody td', true).elements, function(e){
8479 e.on('mouseout', _this.onMouseout, _this);
8481 this.fireEvent('rowsrendered', this);
8487 onUpdate : function(ds,record)
8489 this.refreshRow(record);
8493 onRemove : function(ds, record, index, isUpdate){
8494 if(isUpdate !== true){
8495 this.fireEvent("beforerowremoved", this, index, record);
8497 var bt = this.mainBody.dom;
8499 var rows = this.el.select('tbody > tr', true).elements;
8501 if(typeof(rows[index]) != 'undefined'){
8502 bt.removeChild(rows[index].dom);
8505 // if(bt.rows[index]){
8506 // bt.removeChild(bt.rows[index]);
8509 if(isUpdate !== true){
8510 //this.stripeRows(index);
8511 //this.syncRowHeights(index, index);
8513 this.fireEvent("rowremoved", this, index, record);
8517 onAdd : function(ds, records, rowIndex)
8519 //Roo.log('on Add called');
8520 // - note this does not handle multiple adding very well..
8521 var bt = this.mainBody.dom;
8522 for (var i =0 ; i < records.length;i++) {
8523 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8524 //Roo.log(records[i]);
8525 //Roo.log(this.store.getAt(rowIndex+i));
8526 this.insertRow(this.store, rowIndex + i, false);
8533 refreshRow : function(record){
8534 var ds = this.store, index;
8535 if(typeof record == 'number'){
8537 record = ds.getAt(index);
8539 index = ds.indexOf(record);
8541 return; // should not happen - but seems to
8544 this.insertRow(ds, index, true);
8546 this.onRemove(ds, record, index+1, true);
8548 //this.syncRowHeights(index, index);
8550 this.fireEvent("rowupdated", this, index, record);
8553 insertRow : function(dm, rowIndex, isUpdate){
8556 this.fireEvent("beforerowsinserted", this, rowIndex);
8558 //var s = this.getScrollState();
8559 var row = this.renderRow(this.cm, this.store, rowIndex);
8560 // insert before rowIndex..
8561 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8565 if(row.cellObjects.length){
8566 Roo.each(row.cellObjects, function(r){
8567 _this.renderCellObject(r);
8572 this.fireEvent("rowsinserted", this, rowIndex);
8573 //this.syncRowHeights(firstRow, lastRow);
8574 //this.stripeRows(firstRow);
8581 getRowDom : function(rowIndex)
8583 var rows = this.el.select('tbody > tr', true).elements;
8585 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8588 // returns the object tree for a tr..
8591 renderRow : function(cm, ds, rowIndex)
8593 var d = ds.getAt(rowIndex);
8597 cls : 'x-row-' + rowIndex,
8601 var cellObjects = [];
8603 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8604 var config = cm.config[i];
8606 var renderer = cm.getRenderer(i);
8610 if(typeof(renderer) !== 'undefined'){
8611 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8613 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8614 // and are rendered into the cells after the row is rendered - using the id for the element.
8616 if(typeof(value) === 'object'){
8626 rowIndex : rowIndex,
8631 this.fireEvent('rowclass', this, rowcfg);
8635 cls : rowcfg.rowClass + ' x-col-' + i,
8637 html: (typeof(value) === 'object') ? '' : value
8644 if(typeof(config.colspan) != 'undefined'){
8645 td.colspan = config.colspan;
8648 if(typeof(config.hidden) != 'undefined' && config.hidden){
8649 td.style += ' display:none;';
8652 if(typeof(config.align) != 'undefined' && config.align.length){
8653 td.style += ' text-align:' + config.align + ';';
8655 if(typeof(config.valign) != 'undefined' && config.valign.length){
8656 td.style += ' vertical-align:' + config.valign + ';';
8659 if(typeof(config.width) != 'undefined'){
8660 td.style += ' width:' + config.width + 'px;';
8663 if(typeof(config.cursor) != 'undefined'){
8664 td.style += ' cursor:' + config.cursor + ';';
8667 if(typeof(config.cls) != 'undefined'){
8668 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8671 ['xs','sm','md','lg'].map(function(size){
8673 if(typeof(config[size]) == 'undefined'){
8679 if (!config[size]) { // 0 = hidden
8680 // BS 4 '0' is treated as hide that column and below.
8681 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8685 td.cls += ' col-' + size + '-' + config[size] + (
8686 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8696 row.cellObjects = cellObjects;
8704 onBeforeLoad : function()
8713 this.el.select('tbody', true).first().dom.innerHTML = '';
8716 * Show or hide a row.
8717 * @param {Number} rowIndex to show or hide
8718 * @param {Boolean} state hide
8720 setRowVisibility : function(rowIndex, state)
8722 var bt = this.mainBody.dom;
8724 var rows = this.el.select('tbody > tr', true).elements;
8726 if(typeof(rows[rowIndex]) == 'undefined'){
8729 rows[rowIndex].dom.style.display = state ? '' : 'none';
8733 getSelectionModel : function(){
8735 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8737 return this.selModel;
8740 * Render the Roo.bootstrap object from renderder
8742 renderCellObject : function(r)
8746 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8748 var t = r.cfg.render(r.container);
8751 Roo.each(r.cfg.cn, function(c){
8753 container: t.getChildContainer(),
8756 _this.renderCellObject(child);
8761 getRowIndex : function(row)
8765 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8776 * Returns the grid's underlying element = used by panel.Grid
8777 * @return {Element} The element
8779 getGridEl : function(){
8783 * Forces a resize - used by panel.Grid
8784 * @return {Element} The element
8786 autoSize : function()
8788 //var ctr = Roo.get(this.container.dom.parentElement);
8789 var ctr = Roo.get(this.el.dom);
8791 var thd = this.getGridEl().select('thead',true).first();
8792 var tbd = this.getGridEl().select('tbody', true).first();
8793 var tfd = this.getGridEl().select('tfoot', true).first();
8795 var cw = ctr.getWidth();
8796 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
8800 tbd.setWidth(ctr.getWidth());
8801 // if the body has a max height - and then scrolls - we should perhaps set up the height here
8802 // this needs fixing for various usage - currently only hydra job advers I think..
8804 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8806 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8809 cw = Math.max(cw, this.totalWidth);
8810 this.getGridEl().select('tbody tr',true).setWidth(cw);
8812 // resize 'expandable coloumn?
8814 return; // we doe not have a view in this design..
8817 onBodyScroll: function()
8819 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8821 this.mainHead.setStyle({
8822 'position' : 'relative',
8823 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8829 var scrollHeight = this.mainBody.dom.scrollHeight;
8831 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8833 var height = this.mainBody.getHeight();
8835 if(scrollHeight - height == scrollTop) {
8837 var total = this.ds.getTotalCount();
8839 if(this.footer.cursor + this.footer.pageSize < total){
8841 this.footer.ds.load({
8843 start : this.footer.cursor + this.footer.pageSize,
8844 limit : this.footer.pageSize
8854 onHeaderChange : function()
8856 var header = this.renderHeader();
8857 var table = this.el.select('table', true).first();
8859 this.mainHead.remove();
8860 this.mainHead = table.createChild(header, this.mainBody, false);
8863 onHiddenChange : function(colModel, colIndex, hidden)
8865 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8866 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8868 this.CSS.updateRule(thSelector, "display", "");
8869 this.CSS.updateRule(tdSelector, "display", "");
8872 this.CSS.updateRule(thSelector, "display", "none");
8873 this.CSS.updateRule(tdSelector, "display", "none");
8876 this.onHeaderChange();
8880 setColumnWidth: function(col_index, width)
8882 // width = "md-2 xs-2..."
8883 if(!this.colModel.config[col_index]) {
8887 var w = width.split(" ");
8889 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8891 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8894 for(var j = 0; j < w.length; j++) {
8900 var size_cls = w[j].split("-");
8902 if(!Number.isInteger(size_cls[1] * 1)) {
8906 if(!this.colModel.config[col_index][size_cls[0]]) {
8910 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8914 h_row[0].classList.replace(
8915 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8916 "col-"+size_cls[0]+"-"+size_cls[1]
8919 for(var i = 0; i < rows.length; i++) {
8921 var size_cls = w[j].split("-");
8923 if(!Number.isInteger(size_cls[1] * 1)) {
8927 if(!this.colModel.config[col_index][size_cls[0]]) {
8931 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8935 rows[i].classList.replace(
8936 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8937 "col-"+size_cls[0]+"-"+size_cls[1]
8941 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8956 * @class Roo.bootstrap.TableCell
8957 * @extends Roo.bootstrap.Component
8958 * Bootstrap TableCell class
8959 * @cfg {String} html cell contain text
8960 * @cfg {String} cls cell class
8961 * @cfg {String} tag cell tag (td|th) default td
8962 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8963 * @cfg {String} align Aligns the content in a cell
8964 * @cfg {String} axis Categorizes cells
8965 * @cfg {String} bgcolor Specifies the background color of a cell
8966 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8967 * @cfg {Number} colspan Specifies the number of columns a cell should span
8968 * @cfg {String} headers Specifies one or more header cells a cell is related to
8969 * @cfg {Number} height Sets the height of a cell
8970 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8971 * @cfg {Number} rowspan Sets the number of rows a cell should span
8972 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8973 * @cfg {String} valign Vertical aligns the content in a cell
8974 * @cfg {Number} width Specifies the width of a cell
8977 * Create a new TableCell
8978 * @param {Object} config The config object
8981 Roo.bootstrap.TableCell = function(config){
8982 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8985 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9005 getAutoCreate : function(){
9006 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9026 cfg.align=this.align
9032 cfg.bgcolor=this.bgcolor
9035 cfg.charoff=this.charoff
9038 cfg.colspan=this.colspan
9041 cfg.headers=this.headers
9044 cfg.height=this.height
9047 cfg.nowrap=this.nowrap
9050 cfg.rowspan=this.rowspan
9053 cfg.scope=this.scope
9056 cfg.valign=this.valign
9059 cfg.width=this.width
9078 * @class Roo.bootstrap.TableRow
9079 * @extends Roo.bootstrap.Component
9080 * Bootstrap TableRow class
9081 * @cfg {String} cls row class
9082 * @cfg {String} align Aligns the content in a table row
9083 * @cfg {String} bgcolor Specifies a background color for a table row
9084 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9085 * @cfg {String} valign Vertical aligns the content in a table row
9088 * Create a new TableRow
9089 * @param {Object} config The config object
9092 Roo.bootstrap.TableRow = function(config){
9093 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9096 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9104 getAutoCreate : function(){
9105 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9115 cfg.align = this.align;
9118 cfg.bgcolor = this.bgcolor;
9121 cfg.charoff = this.charoff;
9124 cfg.valign = this.valign;
9142 * @class Roo.bootstrap.TableBody
9143 * @extends Roo.bootstrap.Component
9144 * Bootstrap TableBody class
9145 * @cfg {String} cls element class
9146 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9147 * @cfg {String} align Aligns the content inside the element
9148 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9149 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9152 * Create a new TableBody
9153 * @param {Object} config The config object
9156 Roo.bootstrap.TableBody = function(config){
9157 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9160 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9168 getAutoCreate : function(){
9169 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9183 cfg.align = this.align;
9186 cfg.charoff = this.charoff;
9189 cfg.valign = this.valign;
9196 // initEvents : function()
9203 // this.store = Roo.factory(this.store, Roo.data);
9204 // this.store.on('load', this.onLoad, this);
9206 // this.store.load();
9210 // onLoad: function ()
9212 // this.fireEvent('load', this);
9222 * Ext JS Library 1.1.1
9223 * Copyright(c) 2006-2007, Ext JS, LLC.
9225 * Originally Released Under LGPL - original licence link has changed is not relivant.
9228 * <script type="text/javascript">
9231 // as we use this in bootstrap.
9232 Roo.namespace('Roo.form');
9234 * @class Roo.form.Action
9235 * Internal Class used to handle form actions
9237 * @param {Roo.form.BasicForm} el The form element or its id
9238 * @param {Object} config Configuration options
9243 // define the action interface
9244 Roo.form.Action = function(form, options){
9246 this.options = options || {};
9249 * Client Validation Failed
9252 Roo.form.Action.CLIENT_INVALID = 'client';
9254 * Server Validation Failed
9257 Roo.form.Action.SERVER_INVALID = 'server';
9259 * Connect to Server Failed
9262 Roo.form.Action.CONNECT_FAILURE = 'connect';
9264 * Reading Data from Server Failed
9267 Roo.form.Action.LOAD_FAILURE = 'load';
9269 Roo.form.Action.prototype = {
9271 failureType : undefined,
9272 response : undefined,
9276 run : function(options){
9281 success : function(response){
9286 handleResponse : function(response){
9290 // default connection failure
9291 failure : function(response){
9293 this.response = response;
9294 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9295 this.form.afterAction(this, false);
9298 processResponse : function(response){
9299 this.response = response;
9300 if(!response.responseText){
9303 this.result = this.handleResponse(response);
9307 // utility functions used internally
9308 getUrl : function(appendParams){
9309 var url = this.options.url || this.form.url || this.form.el.dom.action;
9311 var p = this.getParams();
9313 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9319 getMethod : function(){
9320 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9323 getParams : function(){
9324 var bp = this.form.baseParams;
9325 var p = this.options.params;
9327 if(typeof p == "object"){
9328 p = Roo.urlEncode(Roo.applyIf(p, bp));
9329 }else if(typeof p == 'string' && bp){
9330 p += '&' + Roo.urlEncode(bp);
9333 p = Roo.urlEncode(bp);
9338 createCallback : function(){
9340 success: this.success,
9341 failure: this.failure,
9343 timeout: (this.form.timeout*1000),
9344 upload: this.form.fileUpload ? this.success : undefined
9349 Roo.form.Action.Submit = function(form, options){
9350 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9353 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9356 haveProgress : false,
9357 uploadComplete : false,
9359 // uploadProgress indicator.
9360 uploadProgress : function()
9362 if (!this.form.progressUrl) {
9366 if (!this.haveProgress) {
9367 Roo.MessageBox.progress("Uploading", "Uploading");
9369 if (this.uploadComplete) {
9370 Roo.MessageBox.hide();
9374 this.haveProgress = true;
9376 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9378 var c = new Roo.data.Connection();
9380 url : this.form.progressUrl,
9385 success : function(req){
9386 //console.log(data);
9390 rdata = Roo.decode(req.responseText)
9392 Roo.log("Invalid data from server..");
9396 if (!rdata || !rdata.success) {
9398 Roo.MessageBox.alert(Roo.encode(rdata));
9401 var data = rdata.data;
9403 if (this.uploadComplete) {
9404 Roo.MessageBox.hide();
9409 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9410 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9413 this.uploadProgress.defer(2000,this);
9416 failure: function(data) {
9417 Roo.log('progress url failed ');
9428 // run get Values on the form, so it syncs any secondary forms.
9429 this.form.getValues();
9431 var o = this.options;
9432 var method = this.getMethod();
9433 var isPost = method == 'POST';
9434 if(o.clientValidation === false || this.form.isValid()){
9436 if (this.form.progressUrl) {
9437 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9438 (new Date() * 1) + '' + Math.random());
9443 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9444 form:this.form.el.dom,
9445 url:this.getUrl(!isPost),
9447 params:isPost ? this.getParams() : null,
9448 isUpload: this.form.fileUpload,
9449 formData : this.form.formData
9452 this.uploadProgress();
9454 }else if (o.clientValidation !== false){ // client validation failed
9455 this.failureType = Roo.form.Action.CLIENT_INVALID;
9456 this.form.afterAction(this, false);
9460 success : function(response)
9462 this.uploadComplete= true;
9463 if (this.haveProgress) {
9464 Roo.MessageBox.hide();
9468 var result = this.processResponse(response);
9469 if(result === true || result.success){
9470 this.form.afterAction(this, true);
9474 this.form.markInvalid(result.errors);
9475 this.failureType = Roo.form.Action.SERVER_INVALID;
9477 this.form.afterAction(this, false);
9479 failure : function(response)
9481 this.uploadComplete= true;
9482 if (this.haveProgress) {
9483 Roo.MessageBox.hide();
9486 this.response = response;
9487 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9488 this.form.afterAction(this, false);
9491 handleResponse : function(response){
9492 if(this.form.errorReader){
9493 var rs = this.form.errorReader.read(response);
9496 for(var i = 0, len = rs.records.length; i < len; i++) {
9497 var r = rs.records[i];
9501 if(errors.length < 1){
9505 success : rs.success,
9511 ret = Roo.decode(response.responseText);
9515 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9525 Roo.form.Action.Load = function(form, options){
9526 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9527 this.reader = this.form.reader;
9530 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9535 Roo.Ajax.request(Roo.apply(
9536 this.createCallback(), {
9537 method:this.getMethod(),
9538 url:this.getUrl(false),
9539 params:this.getParams()
9543 success : function(response){
9545 var result = this.processResponse(response);
9546 if(result === true || !result.success || !result.data){
9547 this.failureType = Roo.form.Action.LOAD_FAILURE;
9548 this.form.afterAction(this, false);
9551 this.form.clearInvalid();
9552 this.form.setValues(result.data);
9553 this.form.afterAction(this, true);
9556 handleResponse : function(response){
9557 if(this.form.reader){
9558 var rs = this.form.reader.read(response);
9559 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9561 success : rs.success,
9565 return Roo.decode(response.responseText);
9569 Roo.form.Action.ACTION_TYPES = {
9570 'load' : Roo.form.Action.Load,
9571 'submit' : Roo.form.Action.Submit
9580 * @class Roo.bootstrap.Form
9581 * @extends Roo.bootstrap.Component
9582 * Bootstrap Form class
9583 * @cfg {String} method GET | POST (default POST)
9584 * @cfg {String} labelAlign top | left (default top)
9585 * @cfg {String} align left | right - for navbars
9586 * @cfg {Boolean} loadMask load mask when submit (default true)
9591 * @param {Object} config The config object
9595 Roo.bootstrap.Form = function(config){
9597 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9599 Roo.bootstrap.Form.popover.apply();
9603 * @event clientvalidation
9604 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9605 * @param {Form} this
9606 * @param {Boolean} valid true if the form has passed client-side validation
9608 clientvalidation: true,
9610 * @event beforeaction
9611 * Fires before any action is performed. Return false to cancel the action.
9612 * @param {Form} this
9613 * @param {Action} action The action to be performed
9617 * @event actionfailed
9618 * Fires when an action fails.
9619 * @param {Form} this
9620 * @param {Action} action The action that failed
9622 actionfailed : true,
9624 * @event actioncomplete
9625 * Fires when an action is completed.
9626 * @param {Form} this
9627 * @param {Action} action The action that completed
9629 actioncomplete : true
9633 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9636 * @cfg {String} method
9637 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9642 * The URL to use for form actions if one isn't supplied in the action options.
9645 * @cfg {Boolean} fileUpload
9646 * Set to true if this form is a file upload.
9650 * @cfg {Object} baseParams
9651 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9655 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9659 * @cfg {Sting} align (left|right) for navbar forms
9664 activeAction : null,
9667 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9668 * element by passing it or its id or mask the form itself by passing in true.
9671 waitMsgTarget : false,
9676 * @cfg {Boolean} errorMask (true|false) default false
9681 * @cfg {Number} maskOffset Default 100
9686 * @cfg {Boolean} maskBody
9690 getAutoCreate : function(){
9694 method : this.method || 'POST',
9695 id : this.id || Roo.id(),
9698 if (this.parent().xtype.match(/^Nav/)) {
9699 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9703 if (this.labelAlign == 'left' ) {
9704 cfg.cls += ' form-horizontal';
9710 initEvents : function()
9712 this.el.on('submit', this.onSubmit, this);
9713 // this was added as random key presses on the form where triggering form submit.
9714 this.el.on('keypress', function(e) {
9715 if (e.getCharCode() != 13) {
9718 // we might need to allow it for textareas.. and some other items.
9719 // check e.getTarget().
9721 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9725 Roo.log("keypress blocked");
9733 onSubmit : function(e){
9738 * Returns true if client-side validation on the form is successful.
9741 isValid : function(){
9742 var items = this.getItems();
9746 items.each(function(f){
9752 Roo.log('invalid field: ' + f.name);
9756 if(!target && f.el.isVisible(true)){
9762 if(this.errorMask && !valid){
9763 Roo.bootstrap.Form.popover.mask(this, target);
9770 * Returns true if any fields in this form have changed since their original load.
9773 isDirty : function(){
9775 var items = this.getItems();
9776 items.each(function(f){
9786 * Performs a predefined action (submit or load) or custom actions you define on this form.
9787 * @param {String} actionName The name of the action type
9788 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
9789 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9790 * accept other config options):
9792 Property Type Description
9793 ---------------- --------------- ----------------------------------------------------------------------------------
9794 url String The url for the action (defaults to the form's url)
9795 method String The form method to use (defaults to the form's method, or POST if not defined)
9796 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
9797 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
9798 validate the form on the client (defaults to false)
9800 * @return {BasicForm} this
9802 doAction : function(action, options){
9803 if(typeof action == 'string'){
9804 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9806 if(this.fireEvent('beforeaction', this, action) !== false){
9807 this.beforeAction(action);
9808 action.run.defer(100, action);
9814 beforeAction : function(action){
9815 var o = action.options;
9820 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9822 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9825 // not really supported yet.. ??
9827 //if(this.waitMsgTarget === true){
9828 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9829 //}else if(this.waitMsgTarget){
9830 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9831 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9833 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9839 afterAction : function(action, success){
9840 this.activeAction = null;
9841 var o = action.options;
9846 Roo.get(document.body).unmask();
9852 //if(this.waitMsgTarget === true){
9853 // this.el.unmask();
9854 //}else if(this.waitMsgTarget){
9855 // this.waitMsgTarget.unmask();
9857 // Roo.MessageBox.updateProgress(1);
9858 // Roo.MessageBox.hide();
9865 Roo.callback(o.success, o.scope, [this, action]);
9866 this.fireEvent('actioncomplete', this, action);
9870 // failure condition..
9871 // we have a scenario where updates need confirming.
9872 // eg. if a locking scenario exists..
9873 // we look for { errors : { needs_confirm : true }} in the response.
9875 (typeof(action.result) != 'undefined') &&
9876 (typeof(action.result.errors) != 'undefined') &&
9877 (typeof(action.result.errors.needs_confirm) != 'undefined')
9880 Roo.log("not supported yet");
9883 Roo.MessageBox.confirm(
9884 "Change requires confirmation",
9885 action.result.errorMsg,
9890 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
9900 Roo.callback(o.failure, o.scope, [this, action]);
9901 // show an error message if no failed handler is set..
9902 if (!this.hasListener('actionfailed')) {
9903 Roo.log("need to add dialog support");
9905 Roo.MessageBox.alert("Error",
9906 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9907 action.result.errorMsg :
9908 "Saving Failed, please check your entries or try again"
9913 this.fireEvent('actionfailed', this, action);
9918 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9919 * @param {String} id The value to search for
9922 findField : function(id){
9923 var items = this.getItems();
9924 var field = items.get(id);
9926 items.each(function(f){
9927 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9934 return field || null;
9937 * Mark fields in this form invalid in bulk.
9938 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9939 * @return {BasicForm} this
9941 markInvalid : function(errors){
9942 if(errors instanceof Array){
9943 for(var i = 0, len = errors.length; i < len; i++){
9944 var fieldError = errors[i];
9945 var f = this.findField(fieldError.id);
9947 f.markInvalid(fieldError.msg);
9953 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9954 field.markInvalid(errors[id]);
9958 //Roo.each(this.childForms || [], function (f) {
9959 // f.markInvalid(errors);
9966 * Set values for fields in this form in bulk.
9967 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9968 * @return {BasicForm} this
9970 setValues : function(values){
9971 if(values instanceof Array){ // array of objects
9972 for(var i = 0, len = values.length; i < len; i++){
9974 var f = this.findField(v.id);
9976 f.setValue(v.value);
9977 if(this.trackResetOnLoad){
9978 f.originalValue = f.getValue();
9982 }else{ // object hash
9985 if(typeof values[id] != 'function' && (field = this.findField(id))){
9987 if (field.setFromData &&
9989 field.displayField &&
9990 // combos' with local stores can
9991 // be queried via setValue()
9992 // to set their value..
9993 (field.store && !field.store.isLocal)
9997 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9998 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9999 field.setFromData(sd);
10001 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10003 field.setFromData(values);
10006 field.setValue(values[id]);
10010 if(this.trackResetOnLoad){
10011 field.originalValue = field.getValue();
10017 //Roo.each(this.childForms || [], function (f) {
10018 // f.setValues(values);
10025 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10026 * they are returned as an array.
10027 * @param {Boolean} asString
10030 getValues : function(asString){
10031 //if (this.childForms) {
10032 // copy values from the child forms
10033 // Roo.each(this.childForms, function (f) {
10034 // this.setValues(f.getValues());
10040 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10041 if(asString === true){
10044 return Roo.urlDecode(fs);
10048 * Returns the fields in this form as an object with key/value pairs.
10049 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10052 getFieldValues : function(with_hidden)
10054 var items = this.getItems();
10056 items.each(function(f){
10058 if (!f.getName()) {
10062 var v = f.getValue();
10064 if (f.inputType =='radio') {
10065 if (typeof(ret[f.getName()]) == 'undefined') {
10066 ret[f.getName()] = ''; // empty..
10069 if (!f.el.dom.checked) {
10073 v = f.el.dom.value;
10077 if(f.xtype == 'MoneyField'){
10078 ret[f.currencyName] = f.getCurrency();
10081 // not sure if this supported any more..
10082 if ((typeof(v) == 'object') && f.getRawValue) {
10083 v = f.getRawValue() ; // dates..
10085 // combo boxes where name != hiddenName...
10086 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10087 ret[f.name] = f.getRawValue();
10089 ret[f.getName()] = v;
10096 * Clears all invalid messages in this form.
10097 * @return {BasicForm} this
10099 clearInvalid : function(){
10100 var items = this.getItems();
10102 items.each(function(f){
10110 * Resets this form.
10111 * @return {BasicForm} this
10113 reset : function(){
10114 var items = this.getItems();
10115 items.each(function(f){
10119 Roo.each(this.childForms || [], function (f) {
10127 getItems : function()
10129 var r=new Roo.util.MixedCollection(false, function(o){
10130 return o.id || (o.id = Roo.id());
10132 var iter = function(el) {
10139 Roo.each(el.items,function(e) {
10148 hideFields : function(items)
10150 Roo.each(items, function(i){
10152 var f = this.findField(i);
10163 showFields : function(items)
10165 Roo.each(items, function(i){
10167 var f = this.findField(i);
10180 Roo.apply(Roo.bootstrap.Form, {
10196 intervalID : false,
10202 if(this.isApplied){
10207 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10208 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10209 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10210 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10213 this.maskEl.top.enableDisplayMode("block");
10214 this.maskEl.left.enableDisplayMode("block");
10215 this.maskEl.bottom.enableDisplayMode("block");
10216 this.maskEl.right.enableDisplayMode("block");
10218 this.toolTip = new Roo.bootstrap.Tooltip({
10219 cls : 'roo-form-error-popover',
10221 'left' : ['r-l', [-2,0], 'right'],
10222 'right' : ['l-r', [2,0], 'left'],
10223 'bottom' : ['tl-bl', [0,2], 'top'],
10224 'top' : [ 'bl-tl', [0,-2], 'bottom']
10228 this.toolTip.render(Roo.get(document.body));
10230 this.toolTip.el.enableDisplayMode("block");
10232 Roo.get(document.body).on('click', function(){
10236 Roo.get(document.body).on('touchstart', function(){
10240 this.isApplied = true
10243 mask : function(form, target)
10247 this.target = target;
10249 if(!this.form.errorMask || !target.el){
10253 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10255 Roo.log(scrollable);
10257 var ot = this.target.el.calcOffsetsTo(scrollable);
10259 var scrollTo = ot[1] - this.form.maskOffset;
10261 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10263 scrollable.scrollTo('top', scrollTo);
10265 var box = this.target.el.getBox();
10267 var zIndex = Roo.bootstrap.Modal.zIndex++;
10270 this.maskEl.top.setStyle('position', 'absolute');
10271 this.maskEl.top.setStyle('z-index', zIndex);
10272 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10273 this.maskEl.top.setLeft(0);
10274 this.maskEl.top.setTop(0);
10275 this.maskEl.top.show();
10277 this.maskEl.left.setStyle('position', 'absolute');
10278 this.maskEl.left.setStyle('z-index', zIndex);
10279 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10280 this.maskEl.left.setLeft(0);
10281 this.maskEl.left.setTop(box.y - this.padding);
10282 this.maskEl.left.show();
10284 this.maskEl.bottom.setStyle('position', 'absolute');
10285 this.maskEl.bottom.setStyle('z-index', zIndex);
10286 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10287 this.maskEl.bottom.setLeft(0);
10288 this.maskEl.bottom.setTop(box.bottom + this.padding);
10289 this.maskEl.bottom.show();
10291 this.maskEl.right.setStyle('position', 'absolute');
10292 this.maskEl.right.setStyle('z-index', zIndex);
10293 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10294 this.maskEl.right.setLeft(box.right + this.padding);
10295 this.maskEl.right.setTop(box.y - this.padding);
10296 this.maskEl.right.show();
10298 this.toolTip.bindEl = this.target.el;
10300 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10302 var tip = this.target.blankText;
10304 if(this.target.getValue() !== '' ) {
10306 if (this.target.invalidText.length) {
10307 tip = this.target.invalidText;
10308 } else if (this.target.regexText.length){
10309 tip = this.target.regexText;
10313 this.toolTip.show(tip);
10315 this.intervalID = window.setInterval(function() {
10316 Roo.bootstrap.Form.popover.unmask();
10319 window.onwheel = function(){ return false;};
10321 (function(){ this.isMasked = true; }).defer(500, this);
10325 unmask : function()
10327 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10331 this.maskEl.top.setStyle('position', 'absolute');
10332 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10333 this.maskEl.top.hide();
10335 this.maskEl.left.setStyle('position', 'absolute');
10336 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10337 this.maskEl.left.hide();
10339 this.maskEl.bottom.setStyle('position', 'absolute');
10340 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10341 this.maskEl.bottom.hide();
10343 this.maskEl.right.setStyle('position', 'absolute');
10344 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10345 this.maskEl.right.hide();
10347 this.toolTip.hide();
10349 this.toolTip.el.hide();
10351 window.onwheel = function(){ return true;};
10353 if(this.intervalID){
10354 window.clearInterval(this.intervalID);
10355 this.intervalID = false;
10358 this.isMasked = false;
10368 * Ext JS Library 1.1.1
10369 * Copyright(c) 2006-2007, Ext JS, LLC.
10371 * Originally Released Under LGPL - original licence link has changed is not relivant.
10374 * <script type="text/javascript">
10377 * @class Roo.form.VTypes
10378 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10381 Roo.form.VTypes = function(){
10382 // closure these in so they are only created once.
10383 var alpha = /^[a-zA-Z_]+$/;
10384 var alphanum = /^[a-zA-Z0-9_]+$/;
10385 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10386 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10388 // All these messages and functions are configurable
10391 * The function used to validate email addresses
10392 * @param {String} value The email address
10394 'email' : function(v){
10395 return email.test(v);
10398 * The error text to display when the email validation function returns false
10401 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10403 * The keystroke filter mask to be applied on email input
10406 'emailMask' : /[a-z0-9_\.\-@]/i,
10409 * The function used to validate URLs
10410 * @param {String} value The URL
10412 'url' : function(v){
10413 return url.test(v);
10416 * The error text to display when the url validation function returns false
10419 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10422 * The function used to validate alpha values
10423 * @param {String} value The value
10425 'alpha' : function(v){
10426 return alpha.test(v);
10429 * The error text to display when the alpha validation function returns false
10432 'alphaText' : 'This field should only contain letters and _',
10434 * The keystroke filter mask to be applied on alpha input
10437 'alphaMask' : /[a-z_]/i,
10440 * The function used to validate alphanumeric values
10441 * @param {String} value The value
10443 'alphanum' : function(v){
10444 return alphanum.test(v);
10447 * The error text to display when the alphanumeric validation function returns false
10450 'alphanumText' : 'This field should only contain letters, numbers and _',
10452 * The keystroke filter mask to be applied on alphanumeric input
10455 'alphanumMask' : /[a-z0-9_]/i
10465 * @class Roo.bootstrap.Input
10466 * @extends Roo.bootstrap.Component
10467 * Bootstrap Input class
10468 * @cfg {Boolean} disabled is it disabled
10469 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10470 * @cfg {String} name name of the input
10471 * @cfg {string} fieldLabel - the label associated
10472 * @cfg {string} placeholder - placeholder to put in text.
10473 * @cfg {string} before - input group add on before
10474 * @cfg {string} after - input group add on after
10475 * @cfg {string} size - (lg|sm) or leave empty..
10476 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10477 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10478 * @cfg {Number} md colspan out of 12 for computer-sized screens
10479 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10480 * @cfg {string} value default value of the input
10481 * @cfg {Number} labelWidth set the width of label
10482 * @cfg {Number} labellg set the width of label (1-12)
10483 * @cfg {Number} labelmd set the width of label (1-12)
10484 * @cfg {Number} labelsm set the width of label (1-12)
10485 * @cfg {Number} labelxs set the width of label (1-12)
10486 * @cfg {String} labelAlign (top|left)
10487 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10488 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10489 * @cfg {String} indicatorpos (left|right) default left
10490 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10491 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10492 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10494 * @cfg {String} align (left|center|right) Default left
10495 * @cfg {Boolean} forceFeedback (true|false) Default false
10498 * Create a new Input
10499 * @param {Object} config The config object
10502 Roo.bootstrap.Input = function(config){
10504 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10509 * Fires when this field receives input focus.
10510 * @param {Roo.form.Field} this
10515 * Fires when this field loses input focus.
10516 * @param {Roo.form.Field} this
10520 * @event specialkey
10521 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10522 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10523 * @param {Roo.form.Field} this
10524 * @param {Roo.EventObject} e The event object
10529 * Fires just before the field blurs if the field value has changed.
10530 * @param {Roo.form.Field} this
10531 * @param {Mixed} newValue The new value
10532 * @param {Mixed} oldValue The original value
10537 * Fires after the field has been marked as invalid.
10538 * @param {Roo.form.Field} this
10539 * @param {String} msg The validation message
10544 * Fires after the field has been validated with no errors.
10545 * @param {Roo.form.Field} this
10550 * Fires after the key up
10551 * @param {Roo.form.Field} this
10552 * @param {Roo.EventObject} e The event Object
10557 * Fires after the user pastes into input
10558 * @param {Roo.form.Field} this
10559 * @param {Roo.EventObject} e The event Object
10565 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10567 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10568 automatic validation (defaults to "keyup").
10570 validationEvent : "keyup",
10572 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10574 validateOnBlur : true,
10576 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10578 validationDelay : 250,
10580 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10582 focusClass : "x-form-focus", // not needed???
10586 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10588 invalidClass : "has-warning",
10591 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10593 validClass : "has-success",
10596 * @cfg {Boolean} hasFeedback (true|false) default true
10598 hasFeedback : true,
10601 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10603 invalidFeedbackClass : "glyphicon-warning-sign",
10606 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10608 validFeedbackClass : "glyphicon-ok",
10611 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10613 selectOnFocus : false,
10616 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10620 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10625 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10627 disableKeyFilter : false,
10630 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10634 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10638 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10640 blankText : "Please complete this mandatory field",
10643 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10647 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10649 maxLength : Number.MAX_VALUE,
10651 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10653 minLengthText : "The minimum length for this field is {0}",
10655 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10657 maxLengthText : "The maximum length for this field is {0}",
10661 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10662 * If available, this function will be called only after the basic validators all return true, and will be passed the
10663 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10667 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10668 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10669 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10673 * @cfg {String} regexText -- Depricated - use Invalid Text
10678 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10684 autocomplete: false,
10688 inputType : 'text',
10691 placeholder: false,
10696 preventMark: false,
10697 isFormField : true,
10700 labelAlign : false,
10703 formatedValue : false,
10704 forceFeedback : false,
10706 indicatorpos : 'left',
10716 parentLabelAlign : function()
10719 while (parent.parent()) {
10720 parent = parent.parent();
10721 if (typeof(parent.labelAlign) !='undefined') {
10722 return parent.labelAlign;
10729 getAutoCreate : function()
10731 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10737 if(this.inputType != 'hidden'){
10738 cfg.cls = 'form-group' //input-group
10744 type : this.inputType,
10745 value : this.value,
10746 cls : 'form-control',
10747 placeholder : this.placeholder || '',
10748 autocomplete : this.autocomplete || 'new-password'
10750 if (this.inputType == 'file') {
10751 input.style = 'overflow:hidden'; // why not in CSS?
10754 if(this.capture.length){
10755 input.capture = this.capture;
10758 if(this.accept.length){
10759 input.accept = this.accept + "/*";
10763 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10766 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10767 input.maxLength = this.maxLength;
10770 if (this.disabled) {
10771 input.disabled=true;
10774 if (this.readOnly) {
10775 input.readonly=true;
10779 input.name = this.name;
10783 input.cls += ' input-' + this.size;
10787 ['xs','sm','md','lg'].map(function(size){
10788 if (settings[size]) {
10789 cfg.cls += ' col-' + size + '-' + settings[size];
10793 var inputblock = input;
10797 cls: 'glyphicon form-control-feedback'
10800 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10803 cls : 'has-feedback',
10811 if (this.before || this.after) {
10814 cls : 'input-group',
10818 if (this.before && typeof(this.before) == 'string') {
10820 inputblock.cn.push({
10822 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10826 if (this.before && typeof(this.before) == 'object') {
10827 this.before = Roo.factory(this.before);
10829 inputblock.cn.push({
10831 cls : 'roo-input-before input-group-prepend input-group-' +
10832 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10836 inputblock.cn.push(input);
10838 if (this.after && typeof(this.after) == 'string') {
10839 inputblock.cn.push({
10841 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10845 if (this.after && typeof(this.after) == 'object') {
10846 this.after = Roo.factory(this.after);
10848 inputblock.cn.push({
10850 cls : 'roo-input-after input-group-append input-group-' +
10851 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
10855 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10856 inputblock.cls += ' has-feedback';
10857 inputblock.cn.push(feedback);
10862 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10863 tooltip : 'This field is required'
10865 if (this.allowBlank ) {
10866 indicator.style = this.allowBlank ? ' display:none' : '';
10868 if (align ==='left' && this.fieldLabel.length) {
10870 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
10877 cls : 'control-label col-form-label',
10878 html : this.fieldLabel
10889 var labelCfg = cfg.cn[1];
10890 var contentCfg = cfg.cn[2];
10892 if(this.indicatorpos == 'right'){
10897 cls : 'control-label col-form-label',
10901 html : this.fieldLabel
10915 labelCfg = cfg.cn[0];
10916 contentCfg = cfg.cn[1];
10920 if(this.labelWidth > 12){
10921 labelCfg.style = "width: " + this.labelWidth + 'px';
10924 if(this.labelWidth < 13 && this.labelmd == 0){
10925 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
10928 if(this.labellg > 0){
10929 labelCfg.cls += ' col-lg-' + this.labellg;
10930 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10933 if(this.labelmd > 0){
10934 labelCfg.cls += ' col-md-' + this.labelmd;
10935 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10938 if(this.labelsm > 0){
10939 labelCfg.cls += ' col-sm-' + this.labelsm;
10940 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10943 if(this.labelxs > 0){
10944 labelCfg.cls += ' col-xs-' + this.labelxs;
10945 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10949 } else if ( this.fieldLabel.length) {
10956 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10957 tooltip : 'This field is required',
10958 style : this.allowBlank ? ' display:none' : ''
10962 //cls : 'input-group-addon',
10963 html : this.fieldLabel
10971 if(this.indicatorpos == 'right'){
10976 //cls : 'input-group-addon',
10977 html : this.fieldLabel
10982 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10983 tooltip : 'This field is required',
10984 style : this.allowBlank ? ' display:none' : ''
11004 if (this.parentType === 'Navbar' && this.parent().bar) {
11005 cfg.cls += ' navbar-form';
11008 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11009 // on BS4 we do this only if not form
11010 cfg.cls += ' navbar-form';
11018 * return the real input element.
11020 inputEl: function ()
11022 return this.el.select('input.form-control',true).first();
11025 tooltipEl : function()
11027 return this.inputEl();
11030 indicatorEl : function()
11032 if (Roo.bootstrap.version == 4) {
11033 return false; // not enabled in v4 yet.
11036 var indicator = this.el.select('i.roo-required-indicator',true).first();
11046 setDisabled : function(v)
11048 var i = this.inputEl().dom;
11050 i.removeAttribute('disabled');
11054 i.setAttribute('disabled','true');
11056 initEvents : function()
11059 this.inputEl().on("keydown" , this.fireKey, this);
11060 this.inputEl().on("focus", this.onFocus, this);
11061 this.inputEl().on("blur", this.onBlur, this);
11063 this.inputEl().relayEvent('keyup', this);
11064 this.inputEl().relayEvent('paste', this);
11066 this.indicator = this.indicatorEl();
11068 if(this.indicator){
11069 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11072 // reference to original value for reset
11073 this.originalValue = this.getValue();
11074 //Roo.form.TextField.superclass.initEvents.call(this);
11075 if(this.validationEvent == 'keyup'){
11076 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11077 this.inputEl().on('keyup', this.filterValidation, this);
11079 else if(this.validationEvent !== false){
11080 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11083 if(this.selectOnFocus){
11084 this.on("focus", this.preFocus, this);
11087 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11088 this.inputEl().on("keypress", this.filterKeys, this);
11090 this.inputEl().relayEvent('keypress', this);
11093 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11094 this.el.on("click", this.autoSize, this);
11097 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11098 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11101 if (typeof(this.before) == 'object') {
11102 this.before.render(this.el.select('.roo-input-before',true).first());
11104 if (typeof(this.after) == 'object') {
11105 this.after.render(this.el.select('.roo-input-after',true).first());
11108 this.inputEl().on('change', this.onChange, this);
11111 filterValidation : function(e){
11112 if(!e.isNavKeyPress()){
11113 this.validationTask.delay(this.validationDelay);
11117 * Validates the field value
11118 * @return {Boolean} True if the value is valid, else false
11120 validate : function(){
11121 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11122 if(this.disabled || this.validateValue(this.getRawValue())){
11127 this.markInvalid();
11133 * Validates a value according to the field's validation rules and marks the field as invalid
11134 * if the validation fails
11135 * @param {Mixed} value The value to validate
11136 * @return {Boolean} True if the value is valid, else false
11138 validateValue : function(value)
11140 if(this.getVisibilityEl().hasClass('hidden')){
11144 if(value.length < 1) { // if it's blank
11145 if(this.allowBlank){
11151 if(value.length < this.minLength){
11154 if(value.length > this.maxLength){
11158 var vt = Roo.form.VTypes;
11159 if(!vt[this.vtype](value, this)){
11163 if(typeof this.validator == "function"){
11164 var msg = this.validator(value);
11168 if (typeof(msg) == 'string') {
11169 this.invalidText = msg;
11173 if(this.regex && !this.regex.test(value)){
11181 fireKey : function(e){
11182 //Roo.log('field ' + e.getKey());
11183 if(e.isNavKeyPress()){
11184 this.fireEvent("specialkey", this, e);
11187 focus : function (selectText){
11189 this.inputEl().focus();
11190 if(selectText === true){
11191 this.inputEl().dom.select();
11197 onFocus : function(){
11198 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11199 // this.el.addClass(this.focusClass);
11201 if(!this.hasFocus){
11202 this.hasFocus = true;
11203 this.startValue = this.getValue();
11204 this.fireEvent("focus", this);
11208 beforeBlur : Roo.emptyFn,
11212 onBlur : function(){
11214 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11215 //this.el.removeClass(this.focusClass);
11217 this.hasFocus = false;
11218 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11221 var v = this.getValue();
11222 if(String(v) !== String(this.startValue)){
11223 this.fireEvent('change', this, v, this.startValue);
11225 this.fireEvent("blur", this);
11228 onChange : function(e)
11230 var v = this.getValue();
11231 if(String(v) !== String(this.startValue)){
11232 this.fireEvent('change', this, v, this.startValue);
11238 * Resets the current field value to the originally loaded value and clears any validation messages
11240 reset : function(){
11241 this.setValue(this.originalValue);
11245 * Returns the name of the field
11246 * @return {Mixed} name The name field
11248 getName: function(){
11252 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11253 * @return {Mixed} value The field value
11255 getValue : function(){
11257 var v = this.inputEl().getValue();
11262 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11263 * @return {Mixed} value The field value
11265 getRawValue : function(){
11266 var v = this.inputEl().getValue();
11272 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11273 * @param {Mixed} value The value to set
11275 setRawValue : function(v){
11276 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11279 selectText : function(start, end){
11280 var v = this.getRawValue();
11282 start = start === undefined ? 0 : start;
11283 end = end === undefined ? v.length : end;
11284 var d = this.inputEl().dom;
11285 if(d.setSelectionRange){
11286 d.setSelectionRange(start, end);
11287 }else if(d.createTextRange){
11288 var range = d.createTextRange();
11289 range.moveStart("character", start);
11290 range.moveEnd("character", v.length-end);
11297 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11298 * @param {Mixed} value The value to set
11300 setValue : function(v){
11303 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11309 processValue : function(value){
11310 if(this.stripCharsRe){
11311 var newValue = value.replace(this.stripCharsRe, '');
11312 if(newValue !== value){
11313 this.setRawValue(newValue);
11320 preFocus : function(){
11322 if(this.selectOnFocus){
11323 this.inputEl().dom.select();
11326 filterKeys : function(e){
11327 var k = e.getKey();
11328 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11331 var c = e.getCharCode(), cc = String.fromCharCode(c);
11332 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11335 if(!this.maskRe.test(cc)){
11340 * Clear any invalid styles/messages for this field
11342 clearInvalid : function(){
11344 if(!this.el || this.preventMark){ // not rendered
11349 this.el.removeClass([this.invalidClass, 'is-invalid']);
11351 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11353 var feedback = this.el.select('.form-control-feedback', true).first();
11356 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11361 if(this.indicator){
11362 this.indicator.removeClass('visible');
11363 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11366 this.fireEvent('valid', this);
11370 * Mark this field as valid
11372 markValid : function()
11374 if(!this.el || this.preventMark){ // not rendered...
11378 this.el.removeClass([this.invalidClass, this.validClass]);
11379 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11381 var feedback = this.el.select('.form-control-feedback', true).first();
11384 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11387 if(this.indicator){
11388 this.indicator.removeClass('visible');
11389 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11397 if(this.allowBlank && !this.getRawValue().length){
11400 if (Roo.bootstrap.version == 3) {
11401 this.el.addClass(this.validClass);
11403 this.inputEl().addClass('is-valid');
11406 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11408 var feedback = this.el.select('.form-control-feedback', true).first();
11411 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11412 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11417 this.fireEvent('valid', this);
11421 * Mark this field as invalid
11422 * @param {String} msg The validation message
11424 markInvalid : function(msg)
11426 if(!this.el || this.preventMark){ // not rendered
11430 this.el.removeClass([this.invalidClass, this.validClass]);
11431 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11433 var feedback = this.el.select('.form-control-feedback', true).first();
11436 this.el.select('.form-control-feedback', true).first().removeClass(
11437 [this.invalidFeedbackClass, this.validFeedbackClass]);
11444 if(this.allowBlank && !this.getRawValue().length){
11448 if(this.indicator){
11449 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11450 this.indicator.addClass('visible');
11452 if (Roo.bootstrap.version == 3) {
11453 this.el.addClass(this.invalidClass);
11455 this.inputEl().addClass('is-invalid');
11460 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11462 var feedback = this.el.select('.form-control-feedback', true).first();
11465 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11467 if(this.getValue().length || this.forceFeedback){
11468 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11475 this.fireEvent('invalid', this, msg);
11478 SafariOnKeyDown : function(event)
11480 // this is a workaround for a password hang bug on chrome/ webkit.
11481 if (this.inputEl().dom.type != 'password') {
11485 var isSelectAll = false;
11487 if(this.inputEl().dom.selectionEnd > 0){
11488 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11490 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11491 event.preventDefault();
11496 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11498 event.preventDefault();
11499 // this is very hacky as keydown always get's upper case.
11501 var cc = String.fromCharCode(event.getCharCode());
11502 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11506 adjustWidth : function(tag, w){
11507 tag = tag.toLowerCase();
11508 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11509 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11510 if(tag == 'input'){
11513 if(tag == 'textarea'){
11516 }else if(Roo.isOpera){
11517 if(tag == 'input'){
11520 if(tag == 'textarea'){
11528 setFieldLabel : function(v)
11530 if(!this.rendered){
11534 if(this.indicatorEl()){
11535 var ar = this.el.select('label > span',true);
11537 if (ar.elements.length) {
11538 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11539 this.fieldLabel = v;
11543 var br = this.el.select('label',true);
11545 if(br.elements.length) {
11546 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11547 this.fieldLabel = v;
11551 Roo.log('Cannot Found any of label > span || label in input');
11555 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11556 this.fieldLabel = v;
11571 * @class Roo.bootstrap.TextArea
11572 * @extends Roo.bootstrap.Input
11573 * Bootstrap TextArea class
11574 * @cfg {Number} cols Specifies the visible width of a text area
11575 * @cfg {Number} rows Specifies the visible number of lines in a text area
11576 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11577 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11578 * @cfg {string} html text
11581 * Create a new TextArea
11582 * @param {Object} config The config object
11585 Roo.bootstrap.TextArea = function(config){
11586 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11590 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11600 getAutoCreate : function(){
11602 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11608 if(this.inputType != 'hidden'){
11609 cfg.cls = 'form-group' //input-group
11617 value : this.value || '',
11618 html: this.html || '',
11619 cls : 'form-control',
11620 placeholder : this.placeholder || ''
11624 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11625 input.maxLength = this.maxLength;
11629 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11633 input.cols = this.cols;
11636 if (this.readOnly) {
11637 input.readonly = true;
11641 input.name = this.name;
11645 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11649 ['xs','sm','md','lg'].map(function(size){
11650 if (settings[size]) {
11651 cfg.cls += ' col-' + size + '-' + settings[size];
11655 var inputblock = input;
11657 if(this.hasFeedback && !this.allowBlank){
11661 cls: 'glyphicon form-control-feedback'
11665 cls : 'has-feedback',
11674 if (this.before || this.after) {
11677 cls : 'input-group',
11681 inputblock.cn.push({
11683 cls : 'input-group-addon',
11688 inputblock.cn.push(input);
11690 if(this.hasFeedback && !this.allowBlank){
11691 inputblock.cls += ' has-feedback';
11692 inputblock.cn.push(feedback);
11696 inputblock.cn.push({
11698 cls : 'input-group-addon',
11705 if (align ==='left' && this.fieldLabel.length) {
11710 cls : 'control-label',
11711 html : this.fieldLabel
11722 if(this.labelWidth > 12){
11723 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11726 if(this.labelWidth < 13 && this.labelmd == 0){
11727 this.labelmd = this.labelWidth;
11730 if(this.labellg > 0){
11731 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11732 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11735 if(this.labelmd > 0){
11736 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11737 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11740 if(this.labelsm > 0){
11741 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11742 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11745 if(this.labelxs > 0){
11746 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11747 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11750 } else if ( this.fieldLabel.length) {
11755 //cls : 'input-group-addon',
11756 html : this.fieldLabel
11774 if (this.disabled) {
11775 input.disabled=true;
11782 * return the real textarea element.
11784 inputEl: function ()
11786 return this.el.select('textarea.form-control',true).first();
11790 * Clear any invalid styles/messages for this field
11792 clearInvalid : function()
11795 if(!this.el || this.preventMark){ // not rendered
11799 var label = this.el.select('label', true).first();
11800 var icon = this.el.select('i.fa-star', true).first();
11805 this.el.removeClass( this.validClass);
11806 this.inputEl().removeClass('is-invalid');
11808 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11810 var feedback = this.el.select('.form-control-feedback', true).first();
11813 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11818 this.fireEvent('valid', this);
11822 * Mark this field as valid
11824 markValid : function()
11826 if(!this.el || this.preventMark){ // not rendered
11830 this.el.removeClass([this.invalidClass, this.validClass]);
11831 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11833 var feedback = this.el.select('.form-control-feedback', true).first();
11836 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11839 if(this.disabled || this.allowBlank){
11843 var label = this.el.select('label', true).first();
11844 var icon = this.el.select('i.fa-star', true).first();
11849 if (Roo.bootstrap.version == 3) {
11850 this.el.addClass(this.validClass);
11852 this.inputEl().addClass('is-valid');
11856 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11858 var feedback = this.el.select('.form-control-feedback', true).first();
11861 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11862 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11867 this.fireEvent('valid', this);
11871 * Mark this field as invalid
11872 * @param {String} msg The validation message
11874 markInvalid : function(msg)
11876 if(!this.el || this.preventMark){ // not rendered
11880 this.el.removeClass([this.invalidClass, this.validClass]);
11881 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11883 var feedback = this.el.select('.form-control-feedback', true).first();
11886 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11889 if(this.disabled || this.allowBlank){
11893 var label = this.el.select('label', true).first();
11894 var icon = this.el.select('i.fa-star', true).first();
11896 if(!this.getValue().length && label && !icon){
11897 this.el.createChild({
11899 cls : 'text-danger fa fa-lg fa-star',
11900 tooltip : 'This field is required',
11901 style : 'margin-right:5px;'
11905 if (Roo.bootstrap.version == 3) {
11906 this.el.addClass(this.invalidClass);
11908 this.inputEl().addClass('is-invalid');
11911 // fixme ... this may be depricated need to test..
11912 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11914 var feedback = this.el.select('.form-control-feedback', true).first();
11917 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11919 if(this.getValue().length || this.forceFeedback){
11920 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11927 this.fireEvent('invalid', this, msg);
11935 * trigger field - base class for combo..
11940 * @class Roo.bootstrap.TriggerField
11941 * @extends Roo.bootstrap.Input
11942 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11943 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11944 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11945 * for which you can provide a custom implementation. For example:
11947 var trigger = new Roo.bootstrap.TriggerField();
11948 trigger.onTriggerClick = myTriggerFn;
11949 trigger.applyTo('my-field');
11952 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11953 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11954 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
11955 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11956 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11959 * Create a new TriggerField.
11960 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11961 * to the base TextField)
11963 Roo.bootstrap.TriggerField = function(config){
11964 this.mimicing = false;
11965 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11968 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
11970 * @cfg {String} triggerClass A CSS class to apply to the trigger
11973 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11978 * @cfg {Boolean} removable (true|false) special filter default false
11982 /** @cfg {Boolean} grow @hide */
11983 /** @cfg {Number} growMin @hide */
11984 /** @cfg {Number} growMax @hide */
11990 autoSize: Roo.emptyFn,
11994 deferHeight : true,
11997 actionMode : 'wrap',
12002 getAutoCreate : function(){
12004 var align = this.labelAlign || this.parentLabelAlign();
12009 cls: 'form-group' //input-group
12016 type : this.inputType,
12017 cls : 'form-control',
12018 autocomplete: 'new-password',
12019 placeholder : this.placeholder || ''
12023 input.name = this.name;
12026 input.cls += ' input-' + this.size;
12029 if (this.disabled) {
12030 input.disabled=true;
12033 var inputblock = input;
12035 if(this.hasFeedback && !this.allowBlank){
12039 cls: 'glyphicon form-control-feedback'
12042 if(this.removable && !this.editable ){
12044 cls : 'has-feedback',
12050 cls : 'roo-combo-removable-btn close'
12057 cls : 'has-feedback',
12066 if(this.removable && !this.editable ){
12068 cls : 'roo-removable',
12074 cls : 'roo-combo-removable-btn close'
12081 if (this.before || this.after) {
12084 cls : 'input-group',
12088 inputblock.cn.push({
12090 cls : 'input-group-addon input-group-prepend input-group-text',
12095 inputblock.cn.push(input);
12097 if(this.hasFeedback && !this.allowBlank){
12098 inputblock.cls += ' has-feedback';
12099 inputblock.cn.push(feedback);
12103 inputblock.cn.push({
12105 cls : 'input-group-addon input-group-append input-group-text',
12114 var ibwrap = inputblock;
12119 cls: 'roo-select2-choices',
12123 cls: 'roo-select2-search-field',
12135 cls: 'roo-select2-container input-group',
12140 cls: 'form-hidden-field'
12146 if(!this.multiple && this.showToggleBtn){
12152 if (this.caret != false) {
12155 cls: 'fa fa-' + this.caret
12162 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12164 Roo.bootstrap.version == 3 ? caret : '',
12167 cls: 'combobox-clear',
12181 combobox.cls += ' roo-select2-container-multi';
12185 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12186 tooltip : 'This field is required'
12188 if (Roo.bootstrap.version == 4) {
12191 style : 'display:none'
12196 if (align ==='left' && this.fieldLabel.length) {
12198 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12205 cls : 'control-label',
12206 html : this.fieldLabel
12218 var labelCfg = cfg.cn[1];
12219 var contentCfg = cfg.cn[2];
12221 if(this.indicatorpos == 'right'){
12226 cls : 'control-label',
12230 html : this.fieldLabel
12244 labelCfg = cfg.cn[0];
12245 contentCfg = cfg.cn[1];
12248 if(this.labelWidth > 12){
12249 labelCfg.style = "width: " + this.labelWidth + 'px';
12252 if(this.labelWidth < 13 && this.labelmd == 0){
12253 this.labelmd = this.labelWidth;
12256 if(this.labellg > 0){
12257 labelCfg.cls += ' col-lg-' + this.labellg;
12258 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12261 if(this.labelmd > 0){
12262 labelCfg.cls += ' col-md-' + this.labelmd;
12263 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12266 if(this.labelsm > 0){
12267 labelCfg.cls += ' col-sm-' + this.labelsm;
12268 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12271 if(this.labelxs > 0){
12272 labelCfg.cls += ' col-xs-' + this.labelxs;
12273 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12276 } else if ( this.fieldLabel.length) {
12277 // Roo.log(" label");
12282 //cls : 'input-group-addon',
12283 html : this.fieldLabel
12291 if(this.indicatorpos == 'right'){
12299 html : this.fieldLabel
12313 // Roo.log(" no label && no align");
12320 ['xs','sm','md','lg'].map(function(size){
12321 if (settings[size]) {
12322 cfg.cls += ' col-' + size + '-' + settings[size];
12333 onResize : function(w, h){
12334 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12335 // if(typeof w == 'number'){
12336 // var x = w - this.trigger.getWidth();
12337 // this.inputEl().setWidth(this.adjustWidth('input', x));
12338 // this.trigger.setStyle('left', x+'px');
12343 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12346 getResizeEl : function(){
12347 return this.inputEl();
12351 getPositionEl : function(){
12352 return this.inputEl();
12356 alignErrorIcon : function(){
12357 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12361 initEvents : function(){
12365 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12366 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12367 if(!this.multiple && this.showToggleBtn){
12368 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12369 if(this.hideTrigger){
12370 this.trigger.setDisplayed(false);
12372 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12376 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12379 if(this.removable && !this.editable && !this.tickable){
12380 var close = this.closeTriggerEl();
12383 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12384 close.on('click', this.removeBtnClick, this, close);
12388 //this.trigger.addClassOnOver('x-form-trigger-over');
12389 //this.trigger.addClassOnClick('x-form-trigger-click');
12392 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12396 closeTriggerEl : function()
12398 var close = this.el.select('.roo-combo-removable-btn', true).first();
12399 return close ? close : false;
12402 removeBtnClick : function(e, h, el)
12404 e.preventDefault();
12406 if(this.fireEvent("remove", this) !== false){
12408 this.fireEvent("afterremove", this)
12412 createList : function()
12414 this.list = Roo.get(document.body).createChild({
12415 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12416 cls: 'typeahead typeahead-long dropdown-menu shadow',
12417 style: 'display:none'
12420 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12425 initTrigger : function(){
12430 onDestroy : function(){
12432 this.trigger.removeAllListeners();
12433 // this.trigger.remove();
12436 // this.wrap.remove();
12438 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12442 onFocus : function(){
12443 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12445 if(!this.mimicing){
12446 this.wrap.addClass('x-trigger-wrap-focus');
12447 this.mimicing = true;
12448 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12449 if(this.monitorTab){
12450 this.el.on("keydown", this.checkTab, this);
12457 checkTab : function(e){
12458 if(e.getKey() == e.TAB){
12459 this.triggerBlur();
12464 onBlur : function(){
12469 mimicBlur : function(e, t){
12471 if(!this.wrap.contains(t) && this.validateBlur()){
12472 this.triggerBlur();
12478 triggerBlur : function(){
12479 this.mimicing = false;
12480 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12481 if(this.monitorTab){
12482 this.el.un("keydown", this.checkTab, this);
12484 //this.wrap.removeClass('x-trigger-wrap-focus');
12485 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12489 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12490 validateBlur : function(e, t){
12495 onDisable : function(){
12496 this.inputEl().dom.disabled = true;
12497 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12499 // this.wrap.addClass('x-item-disabled');
12504 onEnable : function(){
12505 this.inputEl().dom.disabled = false;
12506 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12508 // this.el.removeClass('x-item-disabled');
12513 onShow : function(){
12514 var ae = this.getActionEl();
12517 ae.dom.style.display = '';
12518 ae.dom.style.visibility = 'visible';
12524 onHide : function(){
12525 var ae = this.getActionEl();
12526 ae.dom.style.display = 'none';
12530 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12531 * by an implementing function.
12533 * @param {EventObject} e
12535 onTriggerClick : Roo.emptyFn
12543 * @class Roo.bootstrap.CardUploader
12544 * @extends Roo.bootstrap.Button
12545 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12546 * @cfg {Number} errorTimeout default 3000
12547 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12548 * @cfg {Array} html The button text.
12552 * Create a new CardUploader
12553 * @param {Object} config The config object
12556 Roo.bootstrap.CardUploader = function(config){
12560 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12563 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12571 * When a image is clicked on - and needs to display a slideshow or similar..
12572 * @param {Roo.bootstrap.Card} this
12573 * @param {Object} The image information data
12579 * When a the download link is clicked
12580 * @param {Roo.bootstrap.Card} this
12581 * @param {Object} The image information data contains
12588 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12591 errorTimeout : 3000,
12595 fileCollection : false,
12598 getAutoCreate : function()
12602 cls :'form-group' ,
12607 //cls : 'input-group-addon',
12608 html : this.fieldLabel
12616 value : this.value,
12617 cls : 'd-none form-control'
12622 multiple : 'multiple',
12624 cls : 'd-none roo-card-upload-selector'
12628 cls : 'roo-card-uploader-button-container w-100 mb-2'
12631 cls : 'card-columns roo-card-uploader-container'
12641 getChildContainer : function() /// what children are added to.
12643 return this.containerEl;
12646 getButtonContainer : function() /// what children are added to.
12648 return this.el.select(".roo-card-uploader-button-container").first();
12651 initEvents : function()
12654 Roo.bootstrap.Input.prototype.initEvents.call(this);
12658 xns: Roo.bootstrap,
12661 container_method : 'getButtonContainer' ,
12662 html : this.html, // fix changable?
12665 'click' : function(btn, e) {
12674 this.urlAPI = (window.createObjectURL && window) ||
12675 (window.URL && URL.revokeObjectURL && URL) ||
12676 (window.webkitURL && webkitURL);
12681 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12683 this.selectorEl.on('change', this.onFileSelected, this);
12686 this.images.forEach(function(img) {
12689 this.images = false;
12691 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12697 onClick : function(e)
12699 e.preventDefault();
12701 this.selectorEl.dom.click();
12705 onFileSelected : function(e)
12707 e.preventDefault();
12709 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12713 Roo.each(this.selectorEl.dom.files, function(file){
12714 this.addFile(file);
12723 addFile : function(file)
12726 if(typeof(file) === 'string'){
12727 throw "Add file by name?"; // should not happen
12731 if(!file || !this.urlAPI){
12741 var url = _this.urlAPI.createObjectURL( file);
12744 id : Roo.bootstrap.CardUploader.ID--,
12745 is_uploaded : false,
12749 mimetype : file.type,
12757 * addCard - add an Attachment to the uploader
12758 * @param data - the data about the image to upload
12762 title : "Title of file",
12763 is_uploaded : false,
12764 src : "http://.....",
12765 srcfile : { the File upload object },
12766 mimetype : file.type,
12769 .. any other data...
12775 addCard : function (data)
12777 // hidden input element?
12778 // if the file is not an image...
12779 //then we need to use something other that and header_image
12784 xns : Roo.bootstrap,
12785 xtype : 'CardFooter',
12788 xns : Roo.bootstrap,
12794 xns : Roo.bootstrap,
12796 html : String.format("<small>{0}</small>", data.title),
12797 cls : 'col-10 text-left',
12802 click : function() {
12804 t.fireEvent( "download", t, data );
12810 xns : Roo.bootstrap,
12812 style: 'max-height: 28px; ',
12818 click : function() {
12819 t.removeCard(data.id)
12831 var cn = this.addxtype(
12834 xns : Roo.bootstrap,
12837 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12838 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
12839 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
12844 initEvents : function() {
12845 Roo.bootstrap.Card.prototype.initEvents.call(this);
12847 this.imgEl = this.el.select('.card-img-top').first();
12849 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12850 this.imgEl.set({ 'pointer' : 'cursor' });
12853 this.getCardFooter().addClass('p-1');
12860 // dont' really need ot update items.
12861 // this.items.push(cn);
12862 this.fileCollection.add(cn);
12864 if (!data.srcfile) {
12865 this.updateInput();
12870 var reader = new FileReader();
12871 reader.addEventListener("load", function() {
12872 data.srcdata = reader.result;
12875 reader.readAsDataURL(data.srcfile);
12880 removeCard : function(id)
12883 var card = this.fileCollection.get(id);
12884 card.data.is_deleted = 1;
12885 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12886 //this.fileCollection.remove(card);
12887 //this.items = this.items.filter(function(e) { return e != card });
12888 // dont' really need ot update items.
12889 card.el.dom.parentNode.removeChild(card.el.dom);
12890 this.updateInput();
12896 this.fileCollection.each(function(card) {
12897 if (card.el.dom && card.el.dom.parentNode) {
12898 card.el.dom.parentNode.removeChild(card.el.dom);
12901 this.fileCollection.clear();
12902 this.updateInput();
12905 updateInput : function()
12908 this.fileCollection.each(function(e) {
12912 this.inputEl().dom.value = JSON.stringify(data);
12922 Roo.bootstrap.CardUploader.ID = -1;/*
12924 * Ext JS Library 1.1.1
12925 * Copyright(c) 2006-2007, Ext JS, LLC.
12927 * Originally Released Under LGPL - original licence link has changed is not relivant.
12930 * <script type="text/javascript">
12935 * @class Roo.data.SortTypes
12937 * Defines the default sorting (casting?) comparison functions used when sorting data.
12939 Roo.data.SortTypes = {
12941 * Default sort that does nothing
12942 * @param {Mixed} s The value being converted
12943 * @return {Mixed} The comparison value
12945 none : function(s){
12950 * The regular expression used to strip tags
12954 stripTagsRE : /<\/?[^>]+>/gi,
12957 * Strips all HTML tags to sort on text only
12958 * @param {Mixed} s The value being converted
12959 * @return {String} The comparison value
12961 asText : function(s){
12962 return String(s).replace(this.stripTagsRE, "");
12966 * Strips all HTML tags to sort on text only - Case insensitive
12967 * @param {Mixed} s The value being converted
12968 * @return {String} The comparison value
12970 asUCText : function(s){
12971 return String(s).toUpperCase().replace(this.stripTagsRE, "");
12975 * Case insensitive string
12976 * @param {Mixed} s The value being converted
12977 * @return {String} The comparison value
12979 asUCString : function(s) {
12980 return String(s).toUpperCase();
12985 * @param {Mixed} s The value being converted
12986 * @return {Number} The comparison value
12988 asDate : function(s) {
12992 if(s instanceof Date){
12993 return s.getTime();
12995 return Date.parse(String(s));
13000 * @param {Mixed} s The value being converted
13001 * @return {Float} The comparison value
13003 asFloat : function(s) {
13004 var val = parseFloat(String(s).replace(/,/g, ""));
13013 * @param {Mixed} s The value being converted
13014 * @return {Number} The comparison value
13016 asInt : function(s) {
13017 var val = parseInt(String(s).replace(/,/g, ""));
13025 * Ext JS Library 1.1.1
13026 * Copyright(c) 2006-2007, Ext JS, LLC.
13028 * Originally Released Under LGPL - original licence link has changed is not relivant.
13031 * <script type="text/javascript">
13035 * @class Roo.data.Record
13036 * Instances of this class encapsulate both record <em>definition</em> information, and record
13037 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13038 * to access Records cached in an {@link Roo.data.Store} object.<br>
13040 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13041 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13044 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13046 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13047 * {@link #create}. The parameters are the same.
13048 * @param {Array} data An associative Array of data values keyed by the field name.
13049 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13050 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13051 * not specified an integer id is generated.
13053 Roo.data.Record = function(data, id){
13054 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13059 * Generate a constructor for a specific record layout.
13060 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13061 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13062 * Each field definition object may contain the following properties: <ul>
13063 * <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,
13064 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13065 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13066 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13067 * is being used, then this is a string containing the javascript expression to reference the data relative to
13068 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13069 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13070 * this may be omitted.</p></li>
13071 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13072 * <ul><li>auto (Default, implies no conversion)</li>
13077 * <li>date</li></ul></p></li>
13078 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13079 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13080 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13081 * by the Reader into an object that will be stored in the Record. It is passed the
13082 * following parameters:<ul>
13083 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13085 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13087 * <br>usage:<br><pre><code>
13088 var TopicRecord = Roo.data.Record.create(
13089 {name: 'title', mapping: 'topic_title'},
13090 {name: 'author', mapping: 'username'},
13091 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13092 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13093 {name: 'lastPoster', mapping: 'user2'},
13094 {name: 'excerpt', mapping: 'post_text'}
13097 var myNewRecord = new TopicRecord({
13098 title: 'Do my job please',
13101 lastPost: new Date(),
13102 lastPoster: 'Animal',
13103 excerpt: 'No way dude!'
13105 myStore.add(myNewRecord);
13110 Roo.data.Record.create = function(o){
13111 var f = function(){
13112 f.superclass.constructor.apply(this, arguments);
13114 Roo.extend(f, Roo.data.Record);
13115 var p = f.prototype;
13116 p.fields = new Roo.util.MixedCollection(false, function(field){
13119 for(var i = 0, len = o.length; i < len; i++){
13120 p.fields.add(new Roo.data.Field(o[i]));
13122 f.getField = function(name){
13123 return p.fields.get(name);
13128 Roo.data.Record.AUTO_ID = 1000;
13129 Roo.data.Record.EDIT = 'edit';
13130 Roo.data.Record.REJECT = 'reject';
13131 Roo.data.Record.COMMIT = 'commit';
13133 Roo.data.Record.prototype = {
13135 * Readonly flag - true if this record has been modified.
13144 join : function(store){
13145 this.store = store;
13149 * Set the named field to the specified value.
13150 * @param {String} name The name of the field to set.
13151 * @param {Object} value The value to set the field to.
13153 set : function(name, value){
13154 if(this.data[name] == value){
13158 if(!this.modified){
13159 this.modified = {};
13161 if(typeof this.modified[name] == 'undefined'){
13162 this.modified[name] = this.data[name];
13164 this.data[name] = value;
13165 if(!this.editing && this.store){
13166 this.store.afterEdit(this);
13171 * Get the value of the named field.
13172 * @param {String} name The name of the field to get the value of.
13173 * @return {Object} The value of the field.
13175 get : function(name){
13176 return this.data[name];
13180 beginEdit : function(){
13181 this.editing = true;
13182 this.modified = {};
13186 cancelEdit : function(){
13187 this.editing = false;
13188 delete this.modified;
13192 endEdit : function(){
13193 this.editing = false;
13194 if(this.dirty && this.store){
13195 this.store.afterEdit(this);
13200 * Usually called by the {@link Roo.data.Store} which owns the Record.
13201 * Rejects all changes made to the Record since either creation, or the last commit operation.
13202 * Modified fields are reverted to their original values.
13204 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13205 * of reject operations.
13207 reject : function(){
13208 var m = this.modified;
13210 if(typeof m[n] != "function"){
13211 this.data[n] = m[n];
13214 this.dirty = false;
13215 delete this.modified;
13216 this.editing = false;
13218 this.store.afterReject(this);
13223 * Usually called by the {@link Roo.data.Store} which owns the Record.
13224 * Commits all changes made to the Record since either creation, or the last commit operation.
13226 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13227 * of commit operations.
13229 commit : function(){
13230 this.dirty = false;
13231 delete this.modified;
13232 this.editing = false;
13234 this.store.afterCommit(this);
13239 hasError : function(){
13240 return this.error != null;
13244 clearError : function(){
13249 * Creates a copy of this record.
13250 * @param {String} id (optional) A new record id if you don't want to use this record's id
13253 copy : function(newId) {
13254 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13258 * Ext JS Library 1.1.1
13259 * Copyright(c) 2006-2007, Ext JS, LLC.
13261 * Originally Released Under LGPL - original licence link has changed is not relivant.
13264 * <script type="text/javascript">
13270 * @class Roo.data.Store
13271 * @extends Roo.util.Observable
13272 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13273 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13275 * 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
13276 * has no knowledge of the format of the data returned by the Proxy.<br>
13278 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13279 * instances from the data object. These records are cached and made available through accessor functions.
13281 * Creates a new Store.
13282 * @param {Object} config A config object containing the objects needed for the Store to access data,
13283 * and read the data into Records.
13285 Roo.data.Store = function(config){
13286 this.data = new Roo.util.MixedCollection(false);
13287 this.data.getKey = function(o){
13290 this.baseParams = {};
13292 this.paramNames = {
13297 "multisort" : "_multisort"
13300 if(config && config.data){
13301 this.inlineData = config.data;
13302 delete config.data;
13305 Roo.apply(this, config);
13307 if(this.reader){ // reader passed
13308 this.reader = Roo.factory(this.reader, Roo.data);
13309 this.reader.xmodule = this.xmodule || false;
13310 if(!this.recordType){
13311 this.recordType = this.reader.recordType;
13313 if(this.reader.onMetaChange){
13314 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13318 if(this.recordType){
13319 this.fields = this.recordType.prototype.fields;
13321 this.modified = [];
13325 * @event datachanged
13326 * Fires when the data cache has changed, and a widget which is using this Store
13327 * as a Record cache should refresh its view.
13328 * @param {Store} this
13330 datachanged : true,
13332 * @event metachange
13333 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13334 * @param {Store} this
13335 * @param {Object} meta The JSON metadata
13340 * Fires when Records have been added to the Store
13341 * @param {Store} this
13342 * @param {Roo.data.Record[]} records The array of Records added
13343 * @param {Number} index The index at which the record(s) were added
13348 * Fires when a Record has been removed from the Store
13349 * @param {Store} this
13350 * @param {Roo.data.Record} record The Record that was removed
13351 * @param {Number} index The index at which the record was removed
13356 * Fires when a Record has been updated
13357 * @param {Store} this
13358 * @param {Roo.data.Record} record The Record that was updated
13359 * @param {String} operation The update operation being performed. Value may be one of:
13361 Roo.data.Record.EDIT
13362 Roo.data.Record.REJECT
13363 Roo.data.Record.COMMIT
13369 * Fires when the data cache has been cleared.
13370 * @param {Store} this
13374 * @event beforeload
13375 * Fires before a request is made for a new data object. If the beforeload handler returns false
13376 * the load action will be canceled.
13377 * @param {Store} this
13378 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13382 * @event beforeloadadd
13383 * Fires after a new set of Records has been loaded.
13384 * @param {Store} this
13385 * @param {Roo.data.Record[]} records The Records that were loaded
13386 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13388 beforeloadadd : true,
13391 * Fires after a new set of Records has been loaded, before they are added to the store.
13392 * @param {Store} this
13393 * @param {Roo.data.Record[]} records The Records that were loaded
13394 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13395 * @params {Object} return from reader
13399 * @event loadexception
13400 * Fires if an exception occurs in the Proxy during loading.
13401 * Called with the signature of the Proxy's "loadexception" event.
13402 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13405 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13406 * @param {Object} load options
13407 * @param {Object} jsonData from your request (normally this contains the Exception)
13409 loadexception : true
13413 this.proxy = Roo.factory(this.proxy, Roo.data);
13414 this.proxy.xmodule = this.xmodule || false;
13415 this.relayEvents(this.proxy, ["loadexception"]);
13417 this.sortToggle = {};
13418 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13420 Roo.data.Store.superclass.constructor.call(this);
13422 if(this.inlineData){
13423 this.loadData(this.inlineData);
13424 delete this.inlineData;
13428 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13430 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13431 * without a remote query - used by combo/forms at present.
13435 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13438 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13441 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13442 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13445 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13446 * on any HTTP request
13449 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13452 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13456 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13457 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13459 remoteSort : false,
13462 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13463 * loaded or when a record is removed. (defaults to false).
13465 pruneModifiedRecords : false,
13468 lastOptions : null,
13471 * Add Records to the Store and fires the add event.
13472 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13474 add : function(records){
13475 records = [].concat(records);
13476 for(var i = 0, len = records.length; i < len; i++){
13477 records[i].join(this);
13479 var index = this.data.length;
13480 this.data.addAll(records);
13481 this.fireEvent("add", this, records, index);
13485 * Remove a Record from the Store and fires the remove event.
13486 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13488 remove : function(record){
13489 var index = this.data.indexOf(record);
13490 this.data.removeAt(index);
13492 if(this.pruneModifiedRecords){
13493 this.modified.remove(record);
13495 this.fireEvent("remove", this, record, index);
13499 * Remove all Records from the Store and fires the clear event.
13501 removeAll : function(){
13503 if(this.pruneModifiedRecords){
13504 this.modified = [];
13506 this.fireEvent("clear", this);
13510 * Inserts Records to the Store at the given index and fires the add event.
13511 * @param {Number} index The start index at which to insert the passed Records.
13512 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13514 insert : function(index, records){
13515 records = [].concat(records);
13516 for(var i = 0, len = records.length; i < len; i++){
13517 this.data.insert(index, records[i]);
13518 records[i].join(this);
13520 this.fireEvent("add", this, records, index);
13524 * Get the index within the cache of the passed Record.
13525 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13526 * @return {Number} The index of the passed Record. Returns -1 if not found.
13528 indexOf : function(record){
13529 return this.data.indexOf(record);
13533 * Get the index within the cache of the Record with the passed id.
13534 * @param {String} id The id of the Record to find.
13535 * @return {Number} The index of the Record. Returns -1 if not found.
13537 indexOfId : function(id){
13538 return this.data.indexOfKey(id);
13542 * Get the Record with the specified id.
13543 * @param {String} id The id of the Record to find.
13544 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13546 getById : function(id){
13547 return this.data.key(id);
13551 * Get the Record at the specified index.
13552 * @param {Number} index The index of the Record to find.
13553 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13555 getAt : function(index){
13556 return this.data.itemAt(index);
13560 * Returns a range of Records between specified indices.
13561 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13562 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13563 * @return {Roo.data.Record[]} An array of Records
13565 getRange : function(start, end){
13566 return this.data.getRange(start, end);
13570 storeOptions : function(o){
13571 o = Roo.apply({}, o);
13574 this.lastOptions = o;
13578 * Loads the Record cache from the configured Proxy using the configured Reader.
13580 * If using remote paging, then the first load call must specify the <em>start</em>
13581 * and <em>limit</em> properties in the options.params property to establish the initial
13582 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13584 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13585 * and this call will return before the new data has been loaded. Perform any post-processing
13586 * in a callback function, or in a "load" event handler.</strong>
13588 * @param {Object} options An object containing properties which control loading options:<ul>
13589 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13590 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13591 * passed the following arguments:<ul>
13592 * <li>r : Roo.data.Record[]</li>
13593 * <li>options: Options object from the load call</li>
13594 * <li>success: Boolean success indicator</li></ul></li>
13595 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13596 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13599 load : function(options){
13600 options = options || {};
13601 if(this.fireEvent("beforeload", this, options) !== false){
13602 this.storeOptions(options);
13603 var p = Roo.apply(options.params || {}, this.baseParams);
13604 // if meta was not loaded from remote source.. try requesting it.
13605 if (!this.reader.metaFromRemote) {
13606 p._requestMeta = 1;
13608 if(this.sortInfo && this.remoteSort){
13609 var pn = this.paramNames;
13610 p[pn["sort"]] = this.sortInfo.field;
13611 p[pn["dir"]] = this.sortInfo.direction;
13613 if (this.multiSort) {
13614 var pn = this.paramNames;
13615 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13618 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13623 * Reloads the Record cache from the configured Proxy using the configured Reader and
13624 * the options from the last load operation performed.
13625 * @param {Object} options (optional) An object containing properties which may override the options
13626 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13627 * the most recently used options are reused).
13629 reload : function(options){
13630 this.load(Roo.applyIf(options||{}, this.lastOptions));
13634 // Called as a callback by the Reader during a load operation.
13635 loadRecords : function(o, options, success){
13636 if(!o || success === false){
13637 if(success !== false){
13638 this.fireEvent("load", this, [], options, o);
13640 if(options.callback){
13641 options.callback.call(options.scope || this, [], options, false);
13645 // if data returned failure - throw an exception.
13646 if (o.success === false) {
13647 // show a message if no listener is registered.
13648 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13649 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13651 // loadmask wil be hooked into this..
13652 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13655 var r = o.records, t = o.totalRecords || r.length;
13657 this.fireEvent("beforeloadadd", this, r, options, o);
13659 if(!options || options.add !== true){
13660 if(this.pruneModifiedRecords){
13661 this.modified = [];
13663 for(var i = 0, len = r.length; i < len; i++){
13667 this.data = this.snapshot;
13668 delete this.snapshot;
13671 this.data.addAll(r);
13672 this.totalLength = t;
13674 this.fireEvent("datachanged", this);
13676 this.totalLength = Math.max(t, this.data.length+r.length);
13680 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13682 var e = new Roo.data.Record({});
13684 e.set(this.parent.displayField, this.parent.emptyTitle);
13685 e.set(this.parent.valueField, '');
13690 this.fireEvent("load", this, r, options, o);
13691 if(options.callback){
13692 options.callback.call(options.scope || this, r, options, true);
13698 * Loads data from a passed data block. A Reader which understands the format of the data
13699 * must have been configured in the constructor.
13700 * @param {Object} data The data block from which to read the Records. The format of the data expected
13701 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13702 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13704 loadData : function(o, append){
13705 var r = this.reader.readRecords(o);
13706 this.loadRecords(r, {add: append}, true);
13710 * using 'cn' the nested child reader read the child array into it's child stores.
13711 * @param {Object} rec The record with a 'children array
13713 loadDataFromChildren : function(rec)
13715 this.loadData(this.reader.toLoadData(rec));
13720 * Gets the number of cached records.
13722 * <em>If using paging, this may not be the total size of the dataset. If the data object
13723 * used by the Reader contains the dataset size, then the getTotalCount() function returns
13724 * the data set size</em>
13726 getCount : function(){
13727 return this.data.length || 0;
13731 * Gets the total number of records in the dataset as returned by the server.
13733 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13734 * the dataset size</em>
13736 getTotalCount : function(){
13737 return this.totalLength || 0;
13741 * Returns the sort state of the Store as an object with two properties:
13743 field {String} The name of the field by which the Records are sorted
13744 direction {String} The sort order, "ASC" or "DESC"
13747 getSortState : function(){
13748 return this.sortInfo;
13752 applySort : function(){
13753 if(this.sortInfo && !this.remoteSort){
13754 var s = this.sortInfo, f = s.field;
13755 var st = this.fields.get(f).sortType;
13756 var fn = function(r1, r2){
13757 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13758 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13760 this.data.sort(s.direction, fn);
13761 if(this.snapshot && this.snapshot != this.data){
13762 this.snapshot.sort(s.direction, fn);
13768 * Sets the default sort column and order to be used by the next load operation.
13769 * @param {String} fieldName The name of the field to sort by.
13770 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13772 setDefaultSort : function(field, dir){
13773 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13777 * Sort the Records.
13778 * If remote sorting is used, the sort is performed on the server, and the cache is
13779 * reloaded. If local sorting is used, the cache is sorted internally.
13780 * @param {String} fieldName The name of the field to sort by.
13781 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13783 sort : function(fieldName, dir){
13784 var f = this.fields.get(fieldName);
13786 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13788 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13789 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13794 this.sortToggle[f.name] = dir;
13795 this.sortInfo = {field: f.name, direction: dir};
13796 if(!this.remoteSort){
13798 this.fireEvent("datachanged", this);
13800 this.load(this.lastOptions);
13805 * Calls the specified function for each of the Records in the cache.
13806 * @param {Function} fn The function to call. The Record is passed as the first parameter.
13807 * Returning <em>false</em> aborts and exits the iteration.
13808 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13810 each : function(fn, scope){
13811 this.data.each(fn, scope);
13815 * Gets all records modified since the last commit. Modified records are persisted across load operations
13816 * (e.g., during paging).
13817 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13819 getModifiedRecords : function(){
13820 return this.modified;
13824 createFilterFn : function(property, value, anyMatch){
13825 if(!value.exec){ // not a regex
13826 value = String(value);
13827 if(value.length == 0){
13830 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13832 return function(r){
13833 return value.test(r.data[property]);
13838 * Sums the value of <i>property</i> for each record between start and end and returns the result.
13839 * @param {String} property A field on your records
13840 * @param {Number} start The record index to start at (defaults to 0)
13841 * @param {Number} end The last record index to include (defaults to length - 1)
13842 * @return {Number} The sum
13844 sum : function(property, start, end){
13845 var rs = this.data.items, v = 0;
13846 start = start || 0;
13847 end = (end || end === 0) ? end : rs.length-1;
13849 for(var i = start; i <= end; i++){
13850 v += (rs[i].data[property] || 0);
13856 * Filter the records by a specified property.
13857 * @param {String} field A field on your records
13858 * @param {String/RegExp} value Either a string that the field
13859 * should start with or a RegExp to test against the field
13860 * @param {Boolean} anyMatch True to match any part not just the beginning
13862 filter : function(property, value, anyMatch){
13863 var fn = this.createFilterFn(property, value, anyMatch);
13864 return fn ? this.filterBy(fn) : this.clearFilter();
13868 * Filter by a function. The specified function will be called with each
13869 * record in this data source. If the function returns true the record is included,
13870 * otherwise it is filtered.
13871 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13872 * @param {Object} scope (optional) The scope of the function (defaults to this)
13874 filterBy : function(fn, scope){
13875 this.snapshot = this.snapshot || this.data;
13876 this.data = this.queryBy(fn, scope||this);
13877 this.fireEvent("datachanged", this);
13881 * Query the records by a specified property.
13882 * @param {String} field A field on your records
13883 * @param {String/RegExp} value Either a string that the field
13884 * should start with or a RegExp to test against the field
13885 * @param {Boolean} anyMatch True to match any part not just the beginning
13886 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13888 query : function(property, value, anyMatch){
13889 var fn = this.createFilterFn(property, value, anyMatch);
13890 return fn ? this.queryBy(fn) : this.data.clone();
13894 * Query by a function. The specified function will be called with each
13895 * record in this data source. If the function returns true the record is included
13897 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13898 * @param {Object} scope (optional) The scope of the function (defaults to this)
13899 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13901 queryBy : function(fn, scope){
13902 var data = this.snapshot || this.data;
13903 return data.filterBy(fn, scope||this);
13907 * Collects unique values for a particular dataIndex from this store.
13908 * @param {String} dataIndex The property to collect
13909 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13910 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13911 * @return {Array} An array of the unique values
13913 collect : function(dataIndex, allowNull, bypassFilter){
13914 var d = (bypassFilter === true && this.snapshot) ?
13915 this.snapshot.items : this.data.items;
13916 var v, sv, r = [], l = {};
13917 for(var i = 0, len = d.length; i < len; i++){
13918 v = d[i].data[dataIndex];
13920 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13929 * Revert to a view of the Record cache with no filtering applied.
13930 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13932 clearFilter : function(suppressEvent){
13933 if(this.snapshot && this.snapshot != this.data){
13934 this.data = this.snapshot;
13935 delete this.snapshot;
13936 if(suppressEvent !== true){
13937 this.fireEvent("datachanged", this);
13943 afterEdit : function(record){
13944 if(this.modified.indexOf(record) == -1){
13945 this.modified.push(record);
13947 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13951 afterReject : function(record){
13952 this.modified.remove(record);
13953 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13957 afterCommit : function(record){
13958 this.modified.remove(record);
13959 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13963 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13964 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13966 commitChanges : function(){
13967 var m = this.modified.slice(0);
13968 this.modified = [];
13969 for(var i = 0, len = m.length; i < len; i++){
13975 * Cancel outstanding changes on all changed records.
13977 rejectChanges : function(){
13978 var m = this.modified.slice(0);
13979 this.modified = [];
13980 for(var i = 0, len = m.length; i < len; i++){
13985 onMetaChange : function(meta, rtype, o){
13986 this.recordType = rtype;
13987 this.fields = rtype.prototype.fields;
13988 delete this.snapshot;
13989 this.sortInfo = meta.sortInfo || this.sortInfo;
13990 this.modified = [];
13991 this.fireEvent('metachange', this, this.reader.meta);
13994 moveIndex : function(data, type)
13996 var index = this.indexOf(data);
13998 var newIndex = index + type;
14002 this.insert(newIndex, data);
14007 * Ext JS Library 1.1.1
14008 * Copyright(c) 2006-2007, Ext JS, LLC.
14010 * Originally Released Under LGPL - original licence link has changed is not relivant.
14013 * <script type="text/javascript">
14017 * @class Roo.data.SimpleStore
14018 * @extends Roo.data.Store
14019 * Small helper class to make creating Stores from Array data easier.
14020 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14021 * @cfg {Array} fields An array of field definition objects, or field name strings.
14022 * @cfg {Object} an existing reader (eg. copied from another store)
14023 * @cfg {Array} data The multi-dimensional array of data
14025 * @param {Object} config
14027 Roo.data.SimpleStore = function(config)
14029 Roo.data.SimpleStore.superclass.constructor.call(this, {
14031 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14034 Roo.data.Record.create(config.fields)
14036 proxy : new Roo.data.MemoryProxy(config.data)
14040 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14042 * Ext JS Library 1.1.1
14043 * Copyright(c) 2006-2007, Ext JS, LLC.
14045 * Originally Released Under LGPL - original licence link has changed is not relivant.
14048 * <script type="text/javascript">
14053 * @extends Roo.data.Store
14054 * @class Roo.data.JsonStore
14055 * Small helper class to make creating Stores for JSON data easier. <br/>
14057 var store = new Roo.data.JsonStore({
14058 url: 'get-images.php',
14060 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14063 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14064 * JsonReader and HttpProxy (unless inline data is provided).</b>
14065 * @cfg {Array} fields An array of field definition objects, or field name strings.
14067 * @param {Object} config
14069 Roo.data.JsonStore = function(c){
14070 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14071 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14072 reader: new Roo.data.JsonReader(c, c.fields)
14075 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14077 * Ext JS Library 1.1.1
14078 * Copyright(c) 2006-2007, Ext JS, LLC.
14080 * Originally Released Under LGPL - original licence link has changed is not relivant.
14083 * <script type="text/javascript">
14087 Roo.data.Field = function(config){
14088 if(typeof config == "string"){
14089 config = {name: config};
14091 Roo.apply(this, config);
14094 this.type = "auto";
14097 var st = Roo.data.SortTypes;
14098 // named sortTypes are supported, here we look them up
14099 if(typeof this.sortType == "string"){
14100 this.sortType = st[this.sortType];
14103 // set default sortType for strings and dates
14104 if(!this.sortType){
14107 this.sortType = st.asUCString;
14110 this.sortType = st.asDate;
14113 this.sortType = st.none;
14118 var stripRe = /[\$,%]/g;
14120 // prebuilt conversion function for this field, instead of
14121 // switching every time we're reading a value
14123 var cv, dateFormat = this.dateFormat;
14128 cv = function(v){ return v; };
14131 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14135 return v !== undefined && v !== null && v !== '' ?
14136 parseInt(String(v).replace(stripRe, ""), 10) : '';
14141 return v !== undefined && v !== null && v !== '' ?
14142 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14147 cv = function(v){ return v === true || v === "true" || v == 1; };
14154 if(v instanceof Date){
14158 if(dateFormat == "timestamp"){
14159 return new Date(v*1000);
14161 return Date.parseDate(v, dateFormat);
14163 var parsed = Date.parse(v);
14164 return parsed ? new Date(parsed) : null;
14173 Roo.data.Field.prototype = {
14181 * Ext JS Library 1.1.1
14182 * Copyright(c) 2006-2007, Ext JS, LLC.
14184 * Originally Released Under LGPL - original licence link has changed is not relivant.
14187 * <script type="text/javascript">
14190 // Base class for reading structured data from a data source. This class is intended to be
14191 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14194 * @class Roo.data.DataReader
14195 * Base class for reading structured data from a data source. This class is intended to be
14196 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14199 Roo.data.DataReader = function(meta, recordType){
14203 this.recordType = recordType instanceof Array ?
14204 Roo.data.Record.create(recordType) : recordType;
14207 Roo.data.DataReader.prototype = {
14210 readerType : 'Data',
14212 * Create an empty record
14213 * @param {Object} data (optional) - overlay some values
14214 * @return {Roo.data.Record} record created.
14216 newRow : function(d) {
14218 this.recordType.prototype.fields.each(function(c) {
14220 case 'int' : da[c.name] = 0; break;
14221 case 'date' : da[c.name] = new Date(); break;
14222 case 'float' : da[c.name] = 0.0; break;
14223 case 'boolean' : da[c.name] = false; break;
14224 default : da[c.name] = ""; break;
14228 return new this.recordType(Roo.apply(da, d));
14234 * Ext JS Library 1.1.1
14235 * Copyright(c) 2006-2007, Ext JS, LLC.
14237 * Originally Released Under LGPL - original licence link has changed is not relivant.
14240 * <script type="text/javascript">
14244 * @class Roo.data.DataProxy
14245 * @extends Roo.data.Observable
14246 * This class is an abstract base class for implementations which provide retrieval of
14247 * unformatted data objects.<br>
14249 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14250 * (of the appropriate type which knows how to parse the data object) to provide a block of
14251 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14253 * Custom implementations must implement the load method as described in
14254 * {@link Roo.data.HttpProxy#load}.
14256 Roo.data.DataProxy = function(){
14259 * @event beforeload
14260 * Fires before a network request is made to retrieve a data object.
14261 * @param {Object} This DataProxy object.
14262 * @param {Object} params The params parameter to the load function.
14267 * Fires before the load method's callback is called.
14268 * @param {Object} This DataProxy object.
14269 * @param {Object} o The data object.
14270 * @param {Object} arg The callback argument object passed to the load function.
14274 * @event loadexception
14275 * Fires if an Exception occurs during data retrieval.
14276 * @param {Object} This DataProxy object.
14277 * @param {Object} o The data object.
14278 * @param {Object} arg The callback argument object passed to the load function.
14279 * @param {Object} e The Exception.
14281 loadexception : true
14283 Roo.data.DataProxy.superclass.constructor.call(this);
14286 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14289 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14293 * Ext JS Library 1.1.1
14294 * Copyright(c) 2006-2007, Ext JS, LLC.
14296 * Originally Released Under LGPL - original licence link has changed is not relivant.
14299 * <script type="text/javascript">
14302 * @class Roo.data.MemoryProxy
14303 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14304 * to the Reader when its load method is called.
14306 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14308 Roo.data.MemoryProxy = function(data){
14312 Roo.data.MemoryProxy.superclass.constructor.call(this);
14316 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14319 * Load data from the requested source (in this case an in-memory
14320 * data object passed to the constructor), read the data object into
14321 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14322 * process that block using the passed callback.
14323 * @param {Object} params This parameter is not used by the MemoryProxy class.
14324 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14325 * object into a block of Roo.data.Records.
14326 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14327 * The function must be passed <ul>
14328 * <li>The Record block object</li>
14329 * <li>The "arg" argument from the load function</li>
14330 * <li>A boolean success indicator</li>
14332 * @param {Object} scope The scope in which to call the callback
14333 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14335 load : function(params, reader, callback, scope, arg){
14336 params = params || {};
14339 result = reader.readRecords(params.data ? params.data :this.data);
14341 this.fireEvent("loadexception", this, arg, null, e);
14342 callback.call(scope, null, arg, false);
14345 callback.call(scope, result, arg, true);
14349 update : function(params, records){
14354 * Ext JS Library 1.1.1
14355 * Copyright(c) 2006-2007, Ext JS, LLC.
14357 * Originally Released Under LGPL - original licence link has changed is not relivant.
14360 * <script type="text/javascript">
14363 * @class Roo.data.HttpProxy
14364 * @extends Roo.data.DataProxy
14365 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14366 * configured to reference a certain URL.<br><br>
14368 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14369 * from which the running page was served.<br><br>
14371 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14373 * Be aware that to enable the browser to parse an XML document, the server must set
14374 * the Content-Type header in the HTTP response to "text/xml".
14376 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14377 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14378 * will be used to make the request.
14380 Roo.data.HttpProxy = function(conn){
14381 Roo.data.HttpProxy.superclass.constructor.call(this);
14382 // is conn a conn config or a real conn?
14384 this.useAjax = !conn || !conn.events;
14388 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14389 // thse are take from connection...
14392 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14395 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14396 * extra parameters to each request made by this object. (defaults to undefined)
14399 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14400 * to each request made by this object. (defaults to undefined)
14403 * @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)
14406 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14409 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14415 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14419 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14420 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14421 * a finer-grained basis than the DataProxy events.
14423 getConnection : function(){
14424 return this.useAjax ? Roo.Ajax : this.conn;
14428 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14429 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14430 * process that block using the passed callback.
14431 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14432 * for the request to the remote server.
14433 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14434 * object into a block of Roo.data.Records.
14435 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14436 * The function must be passed <ul>
14437 * <li>The Record block object</li>
14438 * <li>The "arg" argument from the load function</li>
14439 * <li>A boolean success indicator</li>
14441 * @param {Object} scope The scope in which to call the callback
14442 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14444 load : function(params, reader, callback, scope, arg){
14445 if(this.fireEvent("beforeload", this, params) !== false){
14447 params : params || {},
14449 callback : callback,
14454 callback : this.loadResponse,
14458 Roo.applyIf(o, this.conn);
14459 if(this.activeRequest){
14460 Roo.Ajax.abort(this.activeRequest);
14462 this.activeRequest = Roo.Ajax.request(o);
14464 this.conn.request(o);
14467 callback.call(scope||this, null, arg, false);
14472 loadResponse : function(o, success, response){
14473 delete this.activeRequest;
14475 this.fireEvent("loadexception", this, o, response);
14476 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14481 result = o.reader.read(response);
14483 this.fireEvent("loadexception", this, o, response, e);
14484 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14488 this.fireEvent("load", this, o, o.request.arg);
14489 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14493 update : function(dataSet){
14498 updateResponse : function(dataSet){
14503 * Ext JS Library 1.1.1
14504 * Copyright(c) 2006-2007, Ext JS, LLC.
14506 * Originally Released Under LGPL - original licence link has changed is not relivant.
14509 * <script type="text/javascript">
14513 * @class Roo.data.ScriptTagProxy
14514 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14515 * other than the originating domain of the running page.<br><br>
14517 * <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
14518 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14520 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14521 * source code that is used as the source inside a <script> tag.<br><br>
14523 * In order for the browser to process the returned data, the server must wrap the data object
14524 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14525 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14526 * depending on whether the callback name was passed:
14529 boolean scriptTag = false;
14530 String cb = request.getParameter("callback");
14533 response.setContentType("text/javascript");
14535 response.setContentType("application/x-json");
14537 Writer out = response.getWriter();
14539 out.write(cb + "(");
14541 out.print(dataBlock.toJsonString());
14548 * @param {Object} config A configuration object.
14550 Roo.data.ScriptTagProxy = function(config){
14551 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14552 Roo.apply(this, config);
14553 this.head = document.getElementsByTagName("head")[0];
14556 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14558 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14560 * @cfg {String} url The URL from which to request the data object.
14563 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14567 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14568 * the server the name of the callback function set up by the load call to process the returned data object.
14569 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14570 * javascript output which calls this named function passing the data object as its only parameter.
14572 callbackParam : "callback",
14574 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14575 * name to the request.
14580 * Load data from the configured URL, read the data object into
14581 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14582 * process that block using the passed callback.
14583 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14584 * for the request to the remote server.
14585 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14586 * object into a block of Roo.data.Records.
14587 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14588 * The function must be passed <ul>
14589 * <li>The Record block object</li>
14590 * <li>The "arg" argument from the load function</li>
14591 * <li>A boolean success indicator</li>
14593 * @param {Object} scope The scope in which to call the callback
14594 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14596 load : function(params, reader, callback, scope, arg){
14597 if(this.fireEvent("beforeload", this, params) !== false){
14599 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14601 var url = this.url;
14602 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14604 url += "&_dc=" + (new Date().getTime());
14606 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14609 cb : "stcCallback"+transId,
14610 scriptId : "stcScript"+transId,
14614 callback : callback,
14620 window[trans.cb] = function(o){
14621 conn.handleResponse(o, trans);
14624 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14626 if(this.autoAbort !== false){
14630 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14632 var script = document.createElement("script");
14633 script.setAttribute("src", url);
14634 script.setAttribute("type", "text/javascript");
14635 script.setAttribute("id", trans.scriptId);
14636 this.head.appendChild(script);
14638 this.trans = trans;
14640 callback.call(scope||this, null, arg, false);
14645 isLoading : function(){
14646 return this.trans ? true : false;
14650 * Abort the current server request.
14652 abort : function(){
14653 if(this.isLoading()){
14654 this.destroyTrans(this.trans);
14659 destroyTrans : function(trans, isLoaded){
14660 this.head.removeChild(document.getElementById(trans.scriptId));
14661 clearTimeout(trans.timeoutId);
14663 window[trans.cb] = undefined;
14665 delete window[trans.cb];
14668 // if hasn't been loaded, wait for load to remove it to prevent script error
14669 window[trans.cb] = function(){
14670 window[trans.cb] = undefined;
14672 delete window[trans.cb];
14679 handleResponse : function(o, trans){
14680 this.trans = false;
14681 this.destroyTrans(trans, true);
14684 result = trans.reader.readRecords(o);
14686 this.fireEvent("loadexception", this, o, trans.arg, e);
14687 trans.callback.call(trans.scope||window, null, trans.arg, false);
14690 this.fireEvent("load", this, o, trans.arg);
14691 trans.callback.call(trans.scope||window, result, trans.arg, true);
14695 handleFailure : function(trans){
14696 this.trans = false;
14697 this.destroyTrans(trans, false);
14698 this.fireEvent("loadexception", this, null, trans.arg);
14699 trans.callback.call(trans.scope||window, null, trans.arg, false);
14703 * Ext JS Library 1.1.1
14704 * Copyright(c) 2006-2007, Ext JS, LLC.
14706 * Originally Released Under LGPL - original licence link has changed is not relivant.
14709 * <script type="text/javascript">
14713 * @class Roo.data.JsonReader
14714 * @extends Roo.data.DataReader
14715 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14716 * based on mappings in a provided Roo.data.Record constructor.
14718 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14719 * in the reply previously.
14724 var RecordDef = Roo.data.Record.create([
14725 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
14726 {name: 'occupation'} // This field will use "occupation" as the mapping.
14728 var myReader = new Roo.data.JsonReader({
14729 totalProperty: "results", // The property which contains the total dataset size (optional)
14730 root: "rows", // The property which contains an Array of row objects
14731 id: "id" // The property within each row object that provides an ID for the record (optional)
14735 * This would consume a JSON file like this:
14737 { 'results': 2, 'rows': [
14738 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14739 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14742 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14743 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14744 * paged from the remote server.
14745 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14746 * @cfg {String} root name of the property which contains the Array of row objects.
14747 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14748 * @cfg {Array} fields Array of field definition objects
14750 * Create a new JsonReader
14751 * @param {Object} meta Metadata configuration options
14752 * @param {Object} recordType Either an Array of field definition objects,
14753 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14755 Roo.data.JsonReader = function(meta, recordType){
14758 // set some defaults:
14759 Roo.applyIf(meta, {
14760 totalProperty: 'total',
14761 successProperty : 'success',
14766 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14768 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14770 readerType : 'Json',
14773 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
14774 * Used by Store query builder to append _requestMeta to params.
14777 metaFromRemote : false,
14779 * This method is only used by a DataProxy which has retrieved data from a remote server.
14780 * @param {Object} response The XHR object which contains the JSON data in its responseText.
14781 * @return {Object} data A data block which is used by an Roo.data.Store object as
14782 * a cache of Roo.data.Records.
14784 read : function(response){
14785 var json = response.responseText;
14787 var o = /* eval:var:o */ eval("("+json+")");
14789 throw {message: "JsonReader.read: Json object not found"};
14795 this.metaFromRemote = true;
14796 this.meta = o.metaData;
14797 this.recordType = Roo.data.Record.create(o.metaData.fields);
14798 this.onMetaChange(this.meta, this.recordType, o);
14800 return this.readRecords(o);
14803 // private function a store will implement
14804 onMetaChange : function(meta, recordType, o){
14811 simpleAccess: function(obj, subsc) {
14818 getJsonAccessor: function(){
14820 return function(expr) {
14822 return(re.test(expr))
14823 ? new Function("obj", "return obj." + expr)
14828 return Roo.emptyFn;
14833 * Create a data block containing Roo.data.Records from an XML document.
14834 * @param {Object} o An object which contains an Array of row objects in the property specified
14835 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14836 * which contains the total size of the dataset.
14837 * @return {Object} data A data block which is used by an Roo.data.Store object as
14838 * a cache of Roo.data.Records.
14840 readRecords : function(o){
14842 * After any data loads, the raw JSON data is available for further custom processing.
14846 var s = this.meta, Record = this.recordType,
14847 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14849 // Generate extraction functions for the totalProperty, the root, the id, and for each field
14851 if(s.totalProperty) {
14852 this.getTotal = this.getJsonAccessor(s.totalProperty);
14854 if(s.successProperty) {
14855 this.getSuccess = this.getJsonAccessor(s.successProperty);
14857 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14859 var g = this.getJsonAccessor(s.id);
14860 this.getId = function(rec) {
14862 return (r === undefined || r === "") ? null : r;
14865 this.getId = function(){return null;};
14868 for(var jj = 0; jj < fl; jj++){
14870 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14871 this.ef[jj] = this.getJsonAccessor(map);
14875 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14876 if(s.totalProperty){
14877 var vt = parseInt(this.getTotal(o), 10);
14882 if(s.successProperty){
14883 var vs = this.getSuccess(o);
14884 if(vs === false || vs === 'false'){
14889 for(var i = 0; i < c; i++){
14892 var id = this.getId(n);
14893 for(var j = 0; j < fl; j++){
14895 var v = this.ef[j](n);
14897 Roo.log('missing convert for ' + f.name);
14901 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14903 var record = new Record(values, id);
14905 records[i] = record;
14911 totalRecords : totalRecords
14914 // used when loading children.. @see loadDataFromChildren
14915 toLoadData: function(rec)
14917 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14918 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14919 return { data : data, total : data.length };
14924 * Ext JS Library 1.1.1
14925 * Copyright(c) 2006-2007, Ext JS, LLC.
14927 * Originally Released Under LGPL - original licence link has changed is not relivant.
14930 * <script type="text/javascript">
14934 * @class Roo.data.ArrayReader
14935 * @extends Roo.data.DataReader
14936 * Data reader class to create an Array of Roo.data.Record objects from an Array.
14937 * Each element of that Array represents a row of data fields. The
14938 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14939 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14943 var RecordDef = Roo.data.Record.create([
14944 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
14945 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
14947 var myReader = new Roo.data.ArrayReader({
14948 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
14952 * This would consume an Array like this:
14954 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14958 * Create a new JsonReader
14959 * @param {Object} meta Metadata configuration options.
14960 * @param {Object|Array} recordType Either an Array of field definition objects
14962 * @cfg {Array} fields Array of field definition objects
14963 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14964 * as specified to {@link Roo.data.Record#create},
14965 * or an {@link Roo.data.Record} object
14968 * created using {@link Roo.data.Record#create}.
14970 Roo.data.ArrayReader = function(meta, recordType)
14972 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14975 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14978 * Create a data block containing Roo.data.Records from an XML document.
14979 * @param {Object} o An Array of row objects which represents the dataset.
14980 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14981 * a cache of Roo.data.Records.
14983 readRecords : function(o)
14985 var sid = this.meta ? this.meta.id : null;
14986 var recordType = this.recordType, fields = recordType.prototype.fields;
14989 for(var i = 0; i < root.length; i++){
14992 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14993 for(var j = 0, jlen = fields.length; j < jlen; j++){
14994 var f = fields.items[j];
14995 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14996 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14998 values[f.name] = v;
15000 var record = new recordType(values, id);
15002 records[records.length] = record;
15006 totalRecords : records.length
15009 // used when loading children.. @see loadDataFromChildren
15010 toLoadData: function(rec)
15012 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15013 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15024 * @class Roo.bootstrap.ComboBox
15025 * @extends Roo.bootstrap.TriggerField
15026 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15027 * @cfg {Boolean} append (true|false) default false
15028 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15029 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15030 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15031 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15032 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15033 * @cfg {Boolean} animate default true
15034 * @cfg {Boolean} emptyResultText only for touch device
15035 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15036 * @cfg {String} emptyTitle default ''
15037 * @cfg {Number} width fixed with? experimental
15039 * Create a new ComboBox.
15040 * @param {Object} config Configuration options
15042 Roo.bootstrap.ComboBox = function(config){
15043 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15047 * Fires when the dropdown list is expanded
15048 * @param {Roo.bootstrap.ComboBox} combo This combo box
15053 * Fires when the dropdown list is collapsed
15054 * @param {Roo.bootstrap.ComboBox} combo This combo box
15058 * @event beforeselect
15059 * Fires before a list item is selected. Return false to cancel the selection.
15060 * @param {Roo.bootstrap.ComboBox} combo This combo box
15061 * @param {Roo.data.Record} record The data record returned from the underlying store
15062 * @param {Number} index The index of the selected item in the dropdown list
15064 'beforeselect' : true,
15067 * Fires when a list item is selected
15068 * @param {Roo.bootstrap.ComboBox} combo This combo box
15069 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15070 * @param {Number} index The index of the selected item in the dropdown list
15074 * @event beforequery
15075 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15076 * The event object passed has these properties:
15077 * @param {Roo.bootstrap.ComboBox} combo This combo box
15078 * @param {String} query The query
15079 * @param {Boolean} forceAll true to force "all" query
15080 * @param {Boolean} cancel true to cancel the query
15081 * @param {Object} e The query event object
15083 'beforequery': true,
15086 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15087 * @param {Roo.bootstrap.ComboBox} combo This combo box
15092 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15093 * @param {Roo.bootstrap.ComboBox} combo This combo box
15094 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15099 * Fires when the remove value from the combobox array
15100 * @param {Roo.bootstrap.ComboBox} combo This combo box
15104 * @event afterremove
15105 * Fires when the remove value from the combobox array
15106 * @param {Roo.bootstrap.ComboBox} combo This combo box
15108 'afterremove' : true,
15110 * @event specialfilter
15111 * Fires when specialfilter
15112 * @param {Roo.bootstrap.ComboBox} combo This combo box
15114 'specialfilter' : true,
15117 * Fires when tick the element
15118 * @param {Roo.bootstrap.ComboBox} combo This combo box
15122 * @event touchviewdisplay
15123 * Fires when touch view require special display (default is using displayField)
15124 * @param {Roo.bootstrap.ComboBox} combo This combo box
15125 * @param {Object} cfg set html .
15127 'touchviewdisplay' : true
15132 this.tickItems = [];
15134 this.selectedIndex = -1;
15135 if(this.mode == 'local'){
15136 if(config.queryDelay === undefined){
15137 this.queryDelay = 10;
15139 if(config.minChars === undefined){
15145 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15148 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15149 * rendering into an Roo.Editor, defaults to false)
15152 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15153 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15156 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15159 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15160 * the dropdown list (defaults to undefined, with no header element)
15164 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15168 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15170 listWidth: undefined,
15172 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15173 * mode = 'remote' or 'text' if mode = 'local')
15175 displayField: undefined,
15178 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15179 * mode = 'remote' or 'value' if mode = 'local').
15180 * Note: use of a valueField requires the user make a selection
15181 * in order for a value to be mapped.
15183 valueField: undefined,
15185 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15190 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15191 * field's data value (defaults to the underlying DOM element's name)
15193 hiddenName: undefined,
15195 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15199 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15201 selectedClass: 'active',
15204 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15208 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15209 * anchor positions (defaults to 'tl-bl')
15211 listAlign: 'tl-bl?',
15213 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15217 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15218 * query specified by the allQuery config option (defaults to 'query')
15220 triggerAction: 'query',
15222 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15223 * (defaults to 4, does not apply if editable = false)
15227 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15228 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15232 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15233 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15237 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15238 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15242 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15243 * when editable = true (defaults to false)
15245 selectOnFocus:false,
15247 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15249 queryParam: 'query',
15251 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15252 * when mode = 'remote' (defaults to 'Loading...')
15254 loadingText: 'Loading...',
15256 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15260 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15264 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15265 * traditional select (defaults to true)
15269 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15273 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15277 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15278 * listWidth has a higher value)
15282 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15283 * allow the user to set arbitrary text into the field (defaults to false)
15285 forceSelection:false,
15287 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15288 * if typeAhead = true (defaults to 250)
15290 typeAheadDelay : 250,
15292 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15293 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15295 valueNotFoundText : undefined,
15297 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15299 blockFocus : false,
15302 * @cfg {Boolean} disableClear Disable showing of clear button.
15304 disableClear : false,
15306 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15308 alwaysQuery : false,
15311 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15316 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15318 invalidClass : "has-warning",
15321 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15323 validClass : "has-success",
15326 * @cfg {Boolean} specialFilter (true|false) special filter default false
15328 specialFilter : false,
15331 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15333 mobileTouchView : true,
15336 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15338 useNativeIOS : false,
15341 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15343 mobile_restrict_height : false,
15345 ios_options : false,
15357 btnPosition : 'right',
15358 triggerList : true,
15359 showToggleBtn : true,
15361 emptyResultText: 'Empty',
15362 triggerText : 'Select',
15366 // element that contains real text value.. (when hidden is used..)
15368 getAutoCreate : function()
15373 * Render classic select for iso
15376 if(Roo.isIOS && this.useNativeIOS){
15377 cfg = this.getAutoCreateNativeIOS();
15385 if(Roo.isTouch && this.mobileTouchView){
15386 cfg = this.getAutoCreateTouchView();
15393 if(!this.tickable){
15394 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15399 * ComboBox with tickable selections
15402 var align = this.labelAlign || this.parentLabelAlign();
15405 cls : 'form-group roo-combobox-tickable' //input-group
15408 var btn_text_select = '';
15409 var btn_text_done = '';
15410 var btn_text_cancel = '';
15412 if (this.btn_text_show) {
15413 btn_text_select = 'Select';
15414 btn_text_done = 'Done';
15415 btn_text_cancel = 'Cancel';
15420 cls : 'tickable-buttons',
15425 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15426 //html : this.triggerText
15427 html: btn_text_select
15433 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15435 html: btn_text_done
15441 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15443 html: btn_text_cancel
15449 buttons.cn.unshift({
15451 cls: 'roo-select2-search-field-input'
15457 Roo.each(buttons.cn, function(c){
15459 c.cls += ' btn-' + _this.size;
15462 if (_this.disabled) {
15469 style : 'display: contents',
15474 cls: 'form-hidden-field'
15478 cls: 'roo-select2-choices',
15482 cls: 'roo-select2-search-field',
15493 cls: 'roo-select2-container input-group roo-select2-container-multi',
15499 // cls: 'typeahead typeahead-long dropdown-menu',
15500 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15505 if(this.hasFeedback && !this.allowBlank){
15509 cls: 'glyphicon form-control-feedback'
15512 combobox.cn.push(feedback);
15519 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15520 tooltip : 'This field is required'
15522 if (Roo.bootstrap.version == 4) {
15525 style : 'display:none'
15528 if (align ==='left' && this.fieldLabel.length) {
15530 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15537 cls : 'control-label col-form-label',
15538 html : this.fieldLabel
15550 var labelCfg = cfg.cn[1];
15551 var contentCfg = cfg.cn[2];
15554 if(this.indicatorpos == 'right'){
15560 cls : 'control-label col-form-label',
15564 html : this.fieldLabel
15580 labelCfg = cfg.cn[0];
15581 contentCfg = cfg.cn[1];
15585 if(this.labelWidth > 12){
15586 labelCfg.style = "width: " + this.labelWidth + 'px';
15588 if(this.width * 1 > 0){
15589 contentCfg.style = "width: " + this.width + 'px';
15591 if(this.labelWidth < 13 && this.labelmd == 0){
15592 this.labelmd = this.labelWidth;
15595 if(this.labellg > 0){
15596 labelCfg.cls += ' col-lg-' + this.labellg;
15597 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15600 if(this.labelmd > 0){
15601 labelCfg.cls += ' col-md-' + this.labelmd;
15602 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15605 if(this.labelsm > 0){
15606 labelCfg.cls += ' col-sm-' + this.labelsm;
15607 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15610 if(this.labelxs > 0){
15611 labelCfg.cls += ' col-xs-' + this.labelxs;
15612 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15616 } else if ( this.fieldLabel.length) {
15617 // Roo.log(" label");
15622 //cls : 'input-group-addon',
15623 html : this.fieldLabel
15628 if(this.indicatorpos == 'right'){
15632 //cls : 'input-group-addon',
15633 html : this.fieldLabel
15643 // Roo.log(" no label && no align");
15650 ['xs','sm','md','lg'].map(function(size){
15651 if (settings[size]) {
15652 cfg.cls += ' col-' + size + '-' + settings[size];
15660 _initEventsCalled : false,
15663 initEvents: function()
15665 if (this._initEventsCalled) { // as we call render... prevent looping...
15668 this._initEventsCalled = true;
15671 throw "can not find store for combo";
15674 this.indicator = this.indicatorEl();
15676 this.store = Roo.factory(this.store, Roo.data);
15677 this.store.parent = this;
15679 // if we are building from html. then this element is so complex, that we can not really
15680 // use the rendered HTML.
15681 // so we have to trash and replace the previous code.
15682 if (Roo.XComponent.build_from_html) {
15683 // remove this element....
15684 var e = this.el.dom, k=0;
15685 while (e ) { e = e.previousSibling; ++k;}
15690 this.rendered = false;
15692 this.render(this.parent().getChildContainer(true), k);
15695 if(Roo.isIOS && this.useNativeIOS){
15696 this.initIOSView();
15704 if(Roo.isTouch && this.mobileTouchView){
15705 this.initTouchView();
15710 this.initTickableEvents();
15714 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15716 if(this.hiddenName){
15718 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15720 this.hiddenField.dom.value =
15721 this.hiddenValue !== undefined ? this.hiddenValue :
15722 this.value !== undefined ? this.value : '';
15724 // prevent input submission
15725 this.el.dom.removeAttribute('name');
15726 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15731 // this.el.dom.setAttribute('autocomplete', 'off');
15734 var cls = 'x-combo-list';
15736 //this.list = new Roo.Layer({
15737 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15743 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15744 _this.list.setWidth(lw);
15747 this.list.on('mouseover', this.onViewOver, this);
15748 this.list.on('mousemove', this.onViewMove, this);
15749 this.list.on('scroll', this.onViewScroll, this);
15752 this.list.swallowEvent('mousewheel');
15753 this.assetHeight = 0;
15756 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15757 this.assetHeight += this.header.getHeight();
15760 this.innerList = this.list.createChild({cls:cls+'-inner'});
15761 this.innerList.on('mouseover', this.onViewOver, this);
15762 this.innerList.on('mousemove', this.onViewMove, this);
15763 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15765 if(this.allowBlank && !this.pageSize && !this.disableClear){
15766 this.footer = this.list.createChild({cls:cls+'-ft'});
15767 this.pageTb = new Roo.Toolbar(this.footer);
15771 this.footer = this.list.createChild({cls:cls+'-ft'});
15772 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15773 {pageSize: this.pageSize});
15777 if (this.pageTb && this.allowBlank && !this.disableClear) {
15779 this.pageTb.add(new Roo.Toolbar.Fill(), {
15780 cls: 'x-btn-icon x-btn-clear',
15782 handler: function()
15785 _this.clearValue();
15786 _this.onSelect(false, -1);
15791 this.assetHeight += this.footer.getHeight();
15796 this.tpl = Roo.bootstrap.version == 4 ?
15797 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
15798 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15801 this.view = new Roo.View(this.list, this.tpl, {
15802 singleSelect:true, store: this.store, selectedClass: this.selectedClass
15804 //this.view.wrapEl.setDisplayed(false);
15805 this.view.on('click', this.onViewClick, this);
15808 this.store.on('beforeload', this.onBeforeLoad, this);
15809 this.store.on('load', this.onLoad, this);
15810 this.store.on('loadexception', this.onLoadException, this);
15812 if(this.resizable){
15813 this.resizer = new Roo.Resizable(this.list, {
15814 pinned:true, handles:'se'
15816 this.resizer.on('resize', function(r, w, h){
15817 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15818 this.listWidth = w;
15819 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15820 this.restrictHeight();
15822 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15825 if(!this.editable){
15826 this.editable = true;
15827 this.setEditable(false);
15832 if (typeof(this.events.add.listeners) != 'undefined') {
15834 this.addicon = this.wrap.createChild(
15835 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
15837 this.addicon.on('click', function(e) {
15838 this.fireEvent('add', this);
15841 if (typeof(this.events.edit.listeners) != 'undefined') {
15843 this.editicon = this.wrap.createChild(
15844 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
15845 if (this.addicon) {
15846 this.editicon.setStyle('margin-left', '40px');
15848 this.editicon.on('click', function(e) {
15850 // we fire even if inothing is selected..
15851 this.fireEvent('edit', this, this.lastData );
15857 this.keyNav = new Roo.KeyNav(this.inputEl(), {
15858 "up" : function(e){
15859 this.inKeyMode = true;
15863 "down" : function(e){
15864 if(!this.isExpanded()){
15865 this.onTriggerClick();
15867 this.inKeyMode = true;
15872 "enter" : function(e){
15873 // this.onViewClick();
15877 if(this.fireEvent("specialkey", this, e)){
15878 this.onViewClick(false);
15884 "esc" : function(e){
15888 "tab" : function(e){
15891 if(this.fireEvent("specialkey", this, e)){
15892 this.onViewClick(false);
15900 doRelay : function(foo, bar, hname){
15901 if(hname == 'down' || this.scope.isExpanded()){
15902 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15911 this.queryDelay = Math.max(this.queryDelay || 10,
15912 this.mode == 'local' ? 10 : 250);
15915 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15917 if(this.typeAhead){
15918 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15920 if(this.editable !== false){
15921 this.inputEl().on("keyup", this.onKeyUp, this);
15923 if(this.forceSelection){
15924 this.inputEl().on('blur', this.doForce, this);
15928 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15929 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15933 initTickableEvents: function()
15937 if(this.hiddenName){
15939 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15941 this.hiddenField.dom.value =
15942 this.hiddenValue !== undefined ? this.hiddenValue :
15943 this.value !== undefined ? this.value : '';
15945 // prevent input submission
15946 this.el.dom.removeAttribute('name');
15947 this.hiddenField.dom.setAttribute('name', this.hiddenName);
15952 // this.list = this.el.select('ul.dropdown-menu',true).first();
15954 this.choices = this.el.select('ul.roo-select2-choices', true).first();
15955 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15956 if(this.triggerList){
15957 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15960 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15961 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15963 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15964 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15966 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15967 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15969 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15970 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15971 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15974 this.cancelBtn.hide();
15979 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15980 _this.list.setWidth(lw);
15983 this.list.on('mouseover', this.onViewOver, this);
15984 this.list.on('mousemove', this.onViewMove, this);
15986 this.list.on('scroll', this.onViewScroll, this);
15989 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
15990 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15993 this.view = new Roo.View(this.list, this.tpl, {
15998 selectedClass: this.selectedClass
16001 //this.view.wrapEl.setDisplayed(false);
16002 this.view.on('click', this.onViewClick, this);
16006 this.store.on('beforeload', this.onBeforeLoad, this);
16007 this.store.on('load', this.onLoad, this);
16008 this.store.on('loadexception', this.onLoadException, this);
16011 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16012 "up" : function(e){
16013 this.inKeyMode = true;
16017 "down" : function(e){
16018 this.inKeyMode = true;
16022 "enter" : function(e){
16023 if(this.fireEvent("specialkey", this, e)){
16024 this.onViewClick(false);
16030 "esc" : function(e){
16031 this.onTickableFooterButtonClick(e, false, false);
16034 "tab" : function(e){
16035 this.fireEvent("specialkey", this, e);
16037 this.onTickableFooterButtonClick(e, false, false);
16044 doRelay : function(e, fn, key){
16045 if(this.scope.isExpanded()){
16046 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16055 this.queryDelay = Math.max(this.queryDelay || 10,
16056 this.mode == 'local' ? 10 : 250);
16059 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16061 if(this.typeAhead){
16062 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16065 if(this.editable !== false){
16066 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16069 this.indicator = this.indicatorEl();
16071 if(this.indicator){
16072 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16073 this.indicator.hide();
16078 onDestroy : function(){
16080 this.view.setStore(null);
16081 this.view.el.removeAllListeners();
16082 this.view.el.remove();
16083 this.view.purgeListeners();
16086 this.list.dom.innerHTML = '';
16090 this.store.un('beforeload', this.onBeforeLoad, this);
16091 this.store.un('load', this.onLoad, this);
16092 this.store.un('loadexception', this.onLoadException, this);
16094 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16098 fireKey : function(e){
16099 if(e.isNavKeyPress() && !this.list.isVisible()){
16100 this.fireEvent("specialkey", this, e);
16105 onResize: function(w, h)
16109 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16111 // if(typeof w != 'number'){
16112 // // we do not handle it!?!?
16115 // var tw = this.trigger.getWidth();
16116 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16117 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16119 // this.inputEl().setWidth( this.adjustWidth('input', x));
16121 // //this.trigger.setStyle('left', x+'px');
16123 // if(this.list && this.listWidth === undefined){
16124 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16125 // this.list.setWidth(lw);
16126 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16134 * Allow or prevent the user from directly editing the field text. If false is passed,
16135 * the user will only be able to select from the items defined in the dropdown list. This method
16136 * is the runtime equivalent of setting the 'editable' config option at config time.
16137 * @param {Boolean} value True to allow the user to directly edit the field text
16139 setEditable : function(value){
16140 if(value == this.editable){
16143 this.editable = value;
16145 this.inputEl().dom.setAttribute('readOnly', true);
16146 this.inputEl().on('mousedown', this.onTriggerClick, this);
16147 this.inputEl().addClass('x-combo-noedit');
16149 this.inputEl().dom.setAttribute('readOnly', false);
16150 this.inputEl().un('mousedown', this.onTriggerClick, this);
16151 this.inputEl().removeClass('x-combo-noedit');
16157 onBeforeLoad : function(combo,opts){
16158 if(!this.hasFocus){
16162 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16164 this.restrictHeight();
16165 this.selectedIndex = -1;
16169 onLoad : function(){
16171 this.hasQuery = false;
16173 if(!this.hasFocus){
16177 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16178 this.loading.hide();
16181 if(this.store.getCount() > 0){
16184 this.restrictHeight();
16185 if(this.lastQuery == this.allQuery){
16186 if(this.editable && !this.tickable){
16187 this.inputEl().dom.select();
16191 !this.selectByValue(this.value, true) &&
16194 !this.store.lastOptions ||
16195 typeof(this.store.lastOptions.add) == 'undefined' ||
16196 this.store.lastOptions.add != true
16199 this.select(0, true);
16202 if(this.autoFocus){
16205 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16206 this.taTask.delay(this.typeAheadDelay);
16210 this.onEmptyResults();
16216 onLoadException : function()
16218 this.hasQuery = false;
16220 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16221 this.loading.hide();
16224 if(this.tickable && this.editable){
16229 // only causes errors at present
16230 //Roo.log(this.store.reader.jsonData);
16231 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16233 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16239 onTypeAhead : function(){
16240 if(this.store.getCount() > 0){
16241 var r = this.store.getAt(0);
16242 var newValue = r.data[this.displayField];
16243 var len = newValue.length;
16244 var selStart = this.getRawValue().length;
16246 if(selStart != len){
16247 this.setRawValue(newValue);
16248 this.selectText(selStart, newValue.length);
16254 onSelect : function(record, index){
16256 if(this.fireEvent('beforeselect', this, record, index) !== false){
16258 this.setFromData(index > -1 ? record.data : false);
16261 this.fireEvent('select', this, record, index);
16266 * Returns the currently selected field value or empty string if no value is set.
16267 * @return {String} value The selected value
16269 getValue : function()
16271 if(Roo.isIOS && this.useNativeIOS){
16272 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16276 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16279 if(this.valueField){
16280 return typeof this.value != 'undefined' ? this.value : '';
16282 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16286 getRawValue : function()
16288 if(Roo.isIOS && this.useNativeIOS){
16289 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16292 var v = this.inputEl().getValue();
16298 * Clears any text/value currently set in the field
16300 clearValue : function(){
16302 if(this.hiddenField){
16303 this.hiddenField.dom.value = '';
16306 this.setRawValue('');
16307 this.lastSelectionText = '';
16308 this.lastData = false;
16310 var close = this.closeTriggerEl();
16321 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16322 * will be displayed in the field. If the value does not match the data value of an existing item,
16323 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16324 * Otherwise the field will be blank (although the value will still be set).
16325 * @param {String} value The value to match
16327 setValue : function(v)
16329 if(Roo.isIOS && this.useNativeIOS){
16330 this.setIOSValue(v);
16340 if(this.valueField){
16341 var r = this.findRecord(this.valueField, v);
16343 text = r.data[this.displayField];
16344 }else if(this.valueNotFoundText !== undefined){
16345 text = this.valueNotFoundText;
16348 this.lastSelectionText = text;
16349 if(this.hiddenField){
16350 this.hiddenField.dom.value = v;
16352 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16355 var close = this.closeTriggerEl();
16358 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16364 * @property {Object} the last set data for the element
16369 * Sets the value of the field based on a object which is related to the record format for the store.
16370 * @param {Object} value the value to set as. or false on reset?
16372 setFromData : function(o){
16379 var dv = ''; // display value
16380 var vv = ''; // value value..
16382 if (this.displayField) {
16383 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16385 // this is an error condition!!!
16386 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16389 if(this.valueField){
16390 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16393 var close = this.closeTriggerEl();
16396 if(dv.length || vv * 1 > 0){
16398 this.blockFocus=true;
16404 if(this.hiddenField){
16405 this.hiddenField.dom.value = vv;
16407 this.lastSelectionText = dv;
16408 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16412 // no hidden field.. - we store the value in 'value', but still display
16413 // display field!!!!
16414 this.lastSelectionText = dv;
16415 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16422 reset : function(){
16423 // overridden so that last data is reset..
16430 this.setValue(this.originalValue);
16431 //this.clearInvalid();
16432 this.lastData = false;
16434 this.view.clearSelections();
16440 findRecord : function(prop, value){
16442 if(this.store.getCount() > 0){
16443 this.store.each(function(r){
16444 if(r.data[prop] == value){
16454 getName: function()
16456 // returns hidden if it's set..
16457 if (!this.rendered) {return ''};
16458 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16462 onViewMove : function(e, t){
16463 this.inKeyMode = false;
16467 onViewOver : function(e, t){
16468 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16471 var item = this.view.findItemFromChild(t);
16474 var index = this.view.indexOf(item);
16475 this.select(index, false);
16480 onViewClick : function(view, doFocus, el, e)
16482 var index = this.view.getSelectedIndexes()[0];
16484 var r = this.store.getAt(index);
16488 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16495 Roo.each(this.tickItems, function(v,k){
16497 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16499 _this.tickItems.splice(k, 1);
16501 if(typeof(e) == 'undefined' && view == false){
16502 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16514 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16515 this.tickItems.push(r.data);
16518 if(typeof(e) == 'undefined' && view == false){
16519 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16526 this.onSelect(r, index);
16528 if(doFocus !== false && !this.blockFocus){
16529 this.inputEl().focus();
16534 restrictHeight : function(){
16535 //this.innerList.dom.style.height = '';
16536 //var inner = this.innerList.dom;
16537 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16538 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16539 //this.list.beginUpdate();
16540 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16541 this.list.alignTo(this.inputEl(), this.listAlign);
16542 this.list.alignTo(this.inputEl(), this.listAlign);
16543 //this.list.endUpdate();
16547 onEmptyResults : function(){
16549 if(this.tickable && this.editable){
16550 this.hasFocus = false;
16551 this.restrictHeight();
16559 * Returns true if the dropdown list is expanded, else false.
16561 isExpanded : function(){
16562 return this.list.isVisible();
16566 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16567 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16568 * @param {String} value The data value of the item to select
16569 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16570 * selected item if it is not currently in view (defaults to true)
16571 * @return {Boolean} True if the value matched an item in the list, else false
16573 selectByValue : function(v, scrollIntoView){
16574 if(v !== undefined && v !== null){
16575 var r = this.findRecord(this.valueField || this.displayField, v);
16577 this.select(this.store.indexOf(r), scrollIntoView);
16585 * Select an item in the dropdown list by its numeric index in the list. 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 {Number} index The zero-based index of the list 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)
16591 select : function(index, scrollIntoView){
16592 this.selectedIndex = index;
16593 this.view.select(index);
16594 if(scrollIntoView !== false){
16595 var el = this.view.getNode(index);
16597 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16600 this.list.scrollChildIntoView(el, false);
16606 selectNext : function(){
16607 var ct = this.store.getCount();
16609 if(this.selectedIndex == -1){
16611 }else if(this.selectedIndex < ct-1){
16612 this.select(this.selectedIndex+1);
16618 selectPrev : function(){
16619 var ct = this.store.getCount();
16621 if(this.selectedIndex == -1){
16623 }else if(this.selectedIndex != 0){
16624 this.select(this.selectedIndex-1);
16630 onKeyUp : function(e){
16631 if(this.editable !== false && !e.isSpecialKey()){
16632 this.lastKey = e.getKey();
16633 this.dqTask.delay(this.queryDelay);
16638 validateBlur : function(){
16639 return !this.list || !this.list.isVisible();
16643 initQuery : function(){
16645 var v = this.getRawValue();
16647 if(this.tickable && this.editable){
16648 v = this.tickableInputEl().getValue();
16655 doForce : function(){
16656 if(this.inputEl().dom.value.length > 0){
16657 this.inputEl().dom.value =
16658 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16664 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16665 * query allowing the query action to be canceled if needed.
16666 * @param {String} query The SQL query to execute
16667 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16668 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16669 * saved in the current store (defaults to false)
16671 doQuery : function(q, forceAll){
16673 if(q === undefined || q === null){
16678 forceAll: forceAll,
16682 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16687 forceAll = qe.forceAll;
16688 if(forceAll === true || (q.length >= this.minChars)){
16690 this.hasQuery = true;
16692 if(this.lastQuery != q || this.alwaysQuery){
16693 this.lastQuery = q;
16694 if(this.mode == 'local'){
16695 this.selectedIndex = -1;
16697 this.store.clearFilter();
16700 if(this.specialFilter){
16701 this.fireEvent('specialfilter', this);
16706 this.store.filter(this.displayField, q);
16709 this.store.fireEvent("datachanged", this.store);
16716 this.store.baseParams[this.queryParam] = q;
16718 var options = {params : this.getParams(q)};
16721 options.add = true;
16722 options.params.start = this.page * this.pageSize;
16725 this.store.load(options);
16728 * this code will make the page width larger, at the beginning, the list not align correctly,
16729 * we should expand the list on onLoad
16730 * so command out it
16735 this.selectedIndex = -1;
16740 this.loadNext = false;
16744 getParams : function(q){
16746 //p[this.queryParam] = q;
16750 p.limit = this.pageSize;
16756 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16758 collapse : function(){
16759 if(!this.isExpanded()){
16765 this.hasFocus = false;
16769 this.cancelBtn.hide();
16770 this.trigger.show();
16773 this.tickableInputEl().dom.value = '';
16774 this.tickableInputEl().blur();
16779 Roo.get(document).un('mousedown', this.collapseIf, this);
16780 Roo.get(document).un('mousewheel', this.collapseIf, this);
16781 if (!this.editable) {
16782 Roo.get(document).un('keydown', this.listKeyPress, this);
16784 this.fireEvent('collapse', this);
16790 collapseIf : function(e){
16791 var in_combo = e.within(this.el);
16792 var in_list = e.within(this.list);
16793 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16795 if (in_combo || in_list || is_list) {
16796 //e.stopPropagation();
16801 this.onTickableFooterButtonClick(e, false, false);
16809 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16811 expand : function(){
16813 if(this.isExpanded() || !this.hasFocus){
16817 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16818 this.list.setWidth(lw);
16824 this.restrictHeight();
16828 this.tickItems = Roo.apply([], this.item);
16831 this.cancelBtn.show();
16832 this.trigger.hide();
16835 this.tickableInputEl().focus();
16840 Roo.get(document).on('mousedown', this.collapseIf, this);
16841 Roo.get(document).on('mousewheel', this.collapseIf, this);
16842 if (!this.editable) {
16843 Roo.get(document).on('keydown', this.listKeyPress, this);
16846 this.fireEvent('expand', this);
16850 // Implements the default empty TriggerField.onTriggerClick function
16851 onTriggerClick : function(e)
16853 Roo.log('trigger click');
16855 if(this.disabled || !this.triggerList){
16860 this.loadNext = false;
16862 if(this.isExpanded()){
16864 if (!this.blockFocus) {
16865 this.inputEl().focus();
16869 this.hasFocus = true;
16870 if(this.triggerAction == 'all') {
16871 this.doQuery(this.allQuery, true);
16873 this.doQuery(this.getRawValue());
16875 if (!this.blockFocus) {
16876 this.inputEl().focus();
16881 onTickableTriggerClick : function(e)
16888 this.loadNext = false;
16889 this.hasFocus = true;
16891 if(this.triggerAction == 'all') {
16892 this.doQuery(this.allQuery, true);
16894 this.doQuery(this.getRawValue());
16898 onSearchFieldClick : function(e)
16900 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16901 this.onTickableFooterButtonClick(e, false, false);
16905 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16910 this.loadNext = false;
16911 this.hasFocus = true;
16913 if(this.triggerAction == 'all') {
16914 this.doQuery(this.allQuery, true);
16916 this.doQuery(this.getRawValue());
16920 listKeyPress : function(e)
16922 //Roo.log('listkeypress');
16923 // scroll to first matching element based on key pres..
16924 if (e.isSpecialKey()) {
16927 var k = String.fromCharCode(e.getKey()).toUpperCase();
16930 var csel = this.view.getSelectedNodes();
16931 var cselitem = false;
16933 var ix = this.view.indexOf(csel[0]);
16934 cselitem = this.store.getAt(ix);
16935 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16941 this.store.each(function(v) {
16943 // start at existing selection.
16944 if (cselitem.id == v.id) {
16950 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16951 match = this.store.indexOf(v);
16957 if (match === false) {
16958 return true; // no more action?
16961 this.view.select(match);
16962 var sn = Roo.get(this.view.getSelectedNodes()[0]);
16963 sn.scrollIntoView(sn.dom.parentNode, false);
16966 onViewScroll : function(e, t){
16968 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){
16972 this.hasQuery = true;
16974 this.loading = this.list.select('.loading', true).first();
16976 if(this.loading === null){
16977 this.list.createChild({
16979 cls: 'loading roo-select2-more-results roo-select2-active',
16980 html: 'Loading more results...'
16983 this.loading = this.list.select('.loading', true).first();
16985 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16987 this.loading.hide();
16990 this.loading.show();
16995 this.loadNext = true;
16997 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17002 addItem : function(o)
17004 var dv = ''; // display value
17006 if (this.displayField) {
17007 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17009 // this is an error condition!!!
17010 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17017 var choice = this.choices.createChild({
17019 cls: 'roo-select2-search-choice',
17028 cls: 'roo-select2-search-choice-close fa fa-times',
17033 }, this.searchField);
17035 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17037 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17045 this.inputEl().dom.value = '';
17050 onRemoveItem : function(e, _self, o)
17052 e.preventDefault();
17054 this.lastItem = Roo.apply([], this.item);
17056 var index = this.item.indexOf(o.data) * 1;
17059 Roo.log('not this item?!');
17063 this.item.splice(index, 1);
17068 this.fireEvent('remove', this, e);
17074 syncValue : function()
17076 if(!this.item.length){
17083 Roo.each(this.item, function(i){
17084 if(_this.valueField){
17085 value.push(i[_this.valueField]);
17092 this.value = value.join(',');
17094 if(this.hiddenField){
17095 this.hiddenField.dom.value = this.value;
17098 this.store.fireEvent("datachanged", this.store);
17103 clearItem : function()
17105 if(!this.multiple){
17111 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17119 if(this.tickable && !Roo.isTouch){
17120 this.view.refresh();
17124 inputEl: function ()
17126 if(Roo.isIOS && this.useNativeIOS){
17127 return this.el.select('select.roo-ios-select', true).first();
17130 if(Roo.isTouch && this.mobileTouchView){
17131 return this.el.select('input.form-control',true).first();
17135 return this.searchField;
17138 return this.el.select('input.form-control',true).first();
17141 onTickableFooterButtonClick : function(e, btn, el)
17143 e.preventDefault();
17145 this.lastItem = Roo.apply([], this.item);
17147 if(btn && btn.name == 'cancel'){
17148 this.tickItems = Roo.apply([], this.item);
17157 Roo.each(this.tickItems, function(o){
17165 validate : function()
17167 if(this.getVisibilityEl().hasClass('hidden')){
17171 var v = this.getRawValue();
17174 v = this.getValue();
17177 if(this.disabled || this.allowBlank || v.length){
17182 this.markInvalid();
17186 tickableInputEl : function()
17188 if(!this.tickable || !this.editable){
17189 return this.inputEl();
17192 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17196 getAutoCreateTouchView : function()
17201 cls: 'form-group' //input-group
17207 type : this.inputType,
17208 cls : 'form-control x-combo-noedit',
17209 autocomplete: 'new-password',
17210 placeholder : this.placeholder || '',
17215 input.name = this.name;
17219 input.cls += ' input-' + this.size;
17222 if (this.disabled) {
17223 input.disabled = true;
17227 cls : 'roo-combobox-wrap',
17234 inputblock.cls += ' input-group';
17236 inputblock.cn.unshift({
17238 cls : 'input-group-addon input-group-prepend input-group-text',
17243 if(this.removable && !this.multiple){
17244 inputblock.cls += ' roo-removable';
17246 inputblock.cn.push({
17249 cls : 'roo-combo-removable-btn close'
17253 if(this.hasFeedback && !this.allowBlank){
17255 inputblock.cls += ' has-feedback';
17257 inputblock.cn.push({
17259 cls: 'glyphicon form-control-feedback'
17266 inputblock.cls += (this.before) ? '' : ' input-group';
17268 inputblock.cn.push({
17270 cls : 'input-group-addon input-group-append input-group-text',
17276 var ibwrap = inputblock;
17281 cls: 'roo-select2-choices',
17285 cls: 'roo-select2-search-field',
17298 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17303 cls: 'form-hidden-field'
17309 if(!this.multiple && this.showToggleBtn){
17315 if (this.caret != false) {
17318 cls: 'fa fa-' + this.caret
17325 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17327 Roo.bootstrap.version == 3 ? caret : '',
17330 cls: 'combobox-clear',
17344 combobox.cls += ' roo-select2-container-multi';
17347 var align = this.labelAlign || this.parentLabelAlign();
17349 if (align ==='left' && this.fieldLabel.length) {
17354 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17355 tooltip : 'This field is required'
17359 cls : 'control-label col-form-label',
17360 html : this.fieldLabel
17364 cls : 'roo-combobox-wrap ',
17371 var labelCfg = cfg.cn[1];
17372 var contentCfg = cfg.cn[2];
17375 if(this.indicatorpos == 'right'){
17380 cls : 'control-label col-form-label',
17384 html : this.fieldLabel
17388 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17389 tooltip : 'This field is required'
17394 cls : "roo-combobox-wrap ",
17402 labelCfg = cfg.cn[0];
17403 contentCfg = cfg.cn[1];
17408 if(this.labelWidth > 12){
17409 labelCfg.style = "width: " + this.labelWidth + 'px';
17412 if(this.labelWidth < 13 && this.labelmd == 0){
17413 this.labelmd = this.labelWidth;
17416 if(this.labellg > 0){
17417 labelCfg.cls += ' col-lg-' + this.labellg;
17418 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17421 if(this.labelmd > 0){
17422 labelCfg.cls += ' col-md-' + this.labelmd;
17423 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17426 if(this.labelsm > 0){
17427 labelCfg.cls += ' col-sm-' + this.labelsm;
17428 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17431 if(this.labelxs > 0){
17432 labelCfg.cls += ' col-xs-' + this.labelxs;
17433 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17437 } else if ( this.fieldLabel.length) {
17441 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17442 tooltip : 'This field is required'
17446 cls : 'control-label',
17447 html : this.fieldLabel
17458 if(this.indicatorpos == 'right'){
17462 cls : 'control-label',
17463 html : this.fieldLabel,
17467 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17468 tooltip : 'This field is required'
17485 var settings = this;
17487 ['xs','sm','md','lg'].map(function(size){
17488 if (settings[size]) {
17489 cfg.cls += ' col-' + size + '-' + settings[size];
17496 initTouchView : function()
17498 this.renderTouchView();
17500 this.touchViewEl.on('scroll', function(){
17501 this.el.dom.scrollTop = 0;
17504 this.originalValue = this.getValue();
17506 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17508 this.inputEl().on("click", this.showTouchView, this);
17509 if (this.triggerEl) {
17510 this.triggerEl.on("click", this.showTouchView, this);
17514 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17515 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17517 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17519 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17520 this.store.on('load', this.onTouchViewLoad, this);
17521 this.store.on('loadexception', this.onTouchViewLoadException, this);
17523 if(this.hiddenName){
17525 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17527 this.hiddenField.dom.value =
17528 this.hiddenValue !== undefined ? this.hiddenValue :
17529 this.value !== undefined ? this.value : '';
17531 this.el.dom.removeAttribute('name');
17532 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17536 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17537 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17540 if(this.removable && !this.multiple){
17541 var close = this.closeTriggerEl();
17543 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17544 close.on('click', this.removeBtnClick, this, close);
17548 * fix the bug in Safari iOS8
17550 this.inputEl().on("focus", function(e){
17551 document.activeElement.blur();
17554 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17561 renderTouchView : function()
17563 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17564 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17566 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17567 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17569 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17570 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17571 this.touchViewBodyEl.setStyle('overflow', 'auto');
17573 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17574 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17576 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17577 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17581 showTouchView : function()
17587 this.touchViewHeaderEl.hide();
17589 if(this.modalTitle.length){
17590 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17591 this.touchViewHeaderEl.show();
17594 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17595 this.touchViewEl.show();
17597 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17599 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17600 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17602 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17604 if(this.modalTitle.length){
17605 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17608 this.touchViewBodyEl.setHeight(bodyHeight);
17612 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17614 this.touchViewEl.addClass(['in','show']);
17617 if(this._touchViewMask){
17618 Roo.get(document.body).addClass("x-body-masked");
17619 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17620 this._touchViewMask.setStyle('z-index', 10000);
17621 this._touchViewMask.addClass('show');
17624 this.doTouchViewQuery();
17628 hideTouchView : function()
17630 this.touchViewEl.removeClass(['in','show']);
17634 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17636 this.touchViewEl.setStyle('display', 'none');
17639 if(this._touchViewMask){
17640 this._touchViewMask.removeClass('show');
17641 Roo.get(document.body).removeClass("x-body-masked");
17645 setTouchViewValue : function()
17652 Roo.each(this.tickItems, function(o){
17657 this.hideTouchView();
17660 doTouchViewQuery : function()
17669 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17673 if(!this.alwaysQuery || this.mode == 'local'){
17674 this.onTouchViewLoad();
17681 onTouchViewBeforeLoad : function(combo,opts)
17687 onTouchViewLoad : function()
17689 if(this.store.getCount() < 1){
17690 this.onTouchViewEmptyResults();
17694 this.clearTouchView();
17696 var rawValue = this.getRawValue();
17698 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17700 this.tickItems = [];
17702 this.store.data.each(function(d, rowIndex){
17703 var row = this.touchViewListGroup.createChild(template);
17705 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17706 row.addClass(d.data.cls);
17709 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17712 html : d.data[this.displayField]
17715 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17716 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17719 row.removeClass('selected');
17720 if(!this.multiple && this.valueField &&
17721 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17724 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17725 row.addClass('selected');
17728 if(this.multiple && this.valueField &&
17729 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17733 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17734 this.tickItems.push(d.data);
17737 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17741 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17743 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17745 if(this.modalTitle.length){
17746 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17749 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17751 if(this.mobile_restrict_height && listHeight < bodyHeight){
17752 this.touchViewBodyEl.setHeight(listHeight);
17757 if(firstChecked && listHeight > bodyHeight){
17758 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17763 onTouchViewLoadException : function()
17765 this.hideTouchView();
17768 onTouchViewEmptyResults : function()
17770 this.clearTouchView();
17772 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17774 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17778 clearTouchView : function()
17780 this.touchViewListGroup.dom.innerHTML = '';
17783 onTouchViewClick : function(e, el, o)
17785 e.preventDefault();
17788 var rowIndex = o.rowIndex;
17790 var r = this.store.getAt(rowIndex);
17792 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17794 if(!this.multiple){
17795 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17796 c.dom.removeAttribute('checked');
17799 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17801 this.setFromData(r.data);
17803 var close = this.closeTriggerEl();
17809 this.hideTouchView();
17811 this.fireEvent('select', this, r, rowIndex);
17816 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17817 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17818 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17822 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17823 this.addItem(r.data);
17824 this.tickItems.push(r.data);
17828 getAutoCreateNativeIOS : function()
17831 cls: 'form-group' //input-group,
17836 cls : 'roo-ios-select'
17840 combobox.name = this.name;
17843 if (this.disabled) {
17844 combobox.disabled = true;
17847 var settings = this;
17849 ['xs','sm','md','lg'].map(function(size){
17850 if (settings[size]) {
17851 cfg.cls += ' col-' + size + '-' + settings[size];
17861 initIOSView : function()
17863 this.store.on('load', this.onIOSViewLoad, this);
17868 onIOSViewLoad : function()
17870 if(this.store.getCount() < 1){
17874 this.clearIOSView();
17876 if(this.allowBlank) {
17878 var default_text = '-- SELECT --';
17880 if(this.placeholder.length){
17881 default_text = this.placeholder;
17884 if(this.emptyTitle.length){
17885 default_text += ' - ' + this.emptyTitle + ' -';
17888 var opt = this.inputEl().createChild({
17891 html : default_text
17895 o[this.valueField] = 0;
17896 o[this.displayField] = default_text;
17898 this.ios_options.push({
17905 this.store.data.each(function(d, rowIndex){
17909 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17910 html = d.data[this.displayField];
17915 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17916 value = d.data[this.valueField];
17925 if(this.value == d.data[this.valueField]){
17926 option['selected'] = true;
17929 var opt = this.inputEl().createChild(option);
17931 this.ios_options.push({
17938 this.inputEl().on('change', function(){
17939 this.fireEvent('select', this);
17944 clearIOSView: function()
17946 this.inputEl().dom.innerHTML = '';
17948 this.ios_options = [];
17951 setIOSValue: function(v)
17955 if(!this.ios_options){
17959 Roo.each(this.ios_options, function(opts){
17961 opts.el.dom.removeAttribute('selected');
17963 if(opts.data[this.valueField] != v){
17967 opts.el.dom.setAttribute('selected', true);
17973 * @cfg {Boolean} grow
17977 * @cfg {Number} growMin
17981 * @cfg {Number} growMax
17990 Roo.apply(Roo.bootstrap.ComboBox, {
17994 cls: 'modal-header',
18016 cls: 'list-group-item',
18020 cls: 'roo-combobox-list-group-item-value'
18024 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18038 listItemCheckbox : {
18040 cls: 'list-group-item',
18044 cls: 'roo-combobox-list-group-item-value'
18048 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18064 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18069 cls: 'modal-footer',
18077 cls: 'col-xs-6 text-left',
18080 cls: 'btn btn-danger roo-touch-view-cancel',
18086 cls: 'col-xs-6 text-right',
18089 cls: 'btn btn-success roo-touch-view-ok',
18100 Roo.apply(Roo.bootstrap.ComboBox, {
18102 touchViewTemplate : {
18104 cls: 'modal fade roo-combobox-touch-view',
18108 cls: 'modal-dialog',
18109 style : 'position:fixed', // we have to fix position....
18113 cls: 'modal-content',
18115 Roo.bootstrap.ComboBox.header,
18116 Roo.bootstrap.ComboBox.body,
18117 Roo.bootstrap.ComboBox.footer
18126 * Ext JS Library 1.1.1
18127 * Copyright(c) 2006-2007, Ext JS, LLC.
18129 * Originally Released Under LGPL - original licence link has changed is not relivant.
18132 * <script type="text/javascript">
18137 * @extends Roo.util.Observable
18138 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18139 * This class also supports single and multi selection modes. <br>
18140 * Create a data model bound view:
18142 var store = new Roo.data.Store(...);
18144 var view = new Roo.View({
18146 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18148 singleSelect: true,
18149 selectedClass: "ydataview-selected",
18153 // listen for node click?
18154 view.on("click", function(vw, index, node, e){
18155 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18159 dataModel.load("foobar.xml");
18161 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18163 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18164 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18166 * Note: old style constructor is still suported (container, template, config)
18169 * Create a new View
18170 * @param {Object} config The config object
18173 Roo.View = function(config, depreciated_tpl, depreciated_config){
18175 this.parent = false;
18177 if (typeof(depreciated_tpl) == 'undefined') {
18178 // new way.. - universal constructor.
18179 Roo.apply(this, config);
18180 this.el = Roo.get(this.el);
18183 this.el = Roo.get(config);
18184 this.tpl = depreciated_tpl;
18185 Roo.apply(this, depreciated_config);
18187 this.wrapEl = this.el.wrap().wrap();
18188 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18191 if(typeof(this.tpl) == "string"){
18192 this.tpl = new Roo.Template(this.tpl);
18194 // support xtype ctors..
18195 this.tpl = new Roo.factory(this.tpl, Roo);
18199 this.tpl.compile();
18204 * @event beforeclick
18205 * Fires before a click is processed. Returns false to cancel the default action.
18206 * @param {Roo.View} this
18207 * @param {Number} index The index of the target node
18208 * @param {HTMLElement} node The target node
18209 * @param {Roo.EventObject} e The raw event object
18211 "beforeclick" : true,
18214 * Fires when a template node is clicked.
18215 * @param {Roo.View} this
18216 * @param {Number} index The index of the target node
18217 * @param {HTMLElement} node The target node
18218 * @param {Roo.EventObject} e The raw event object
18223 * Fires when a template node is double clicked.
18224 * @param {Roo.View} this
18225 * @param {Number} index The index of the target node
18226 * @param {HTMLElement} node The target node
18227 * @param {Roo.EventObject} e The raw event object
18231 * @event contextmenu
18232 * Fires when a template node is right clicked.
18233 * @param {Roo.View} this
18234 * @param {Number} index The index of the target node
18235 * @param {HTMLElement} node The target node
18236 * @param {Roo.EventObject} e The raw event object
18238 "contextmenu" : true,
18240 * @event selectionchange
18241 * Fires when the selected nodes change.
18242 * @param {Roo.View} this
18243 * @param {Array} selections Array of the selected nodes
18245 "selectionchange" : true,
18248 * @event beforeselect
18249 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18250 * @param {Roo.View} this
18251 * @param {HTMLElement} node The node to be selected
18252 * @param {Array} selections Array of currently selected nodes
18254 "beforeselect" : true,
18256 * @event preparedata
18257 * Fires on every row to render, to allow you to change the data.
18258 * @param {Roo.View} this
18259 * @param {Object} data to be rendered (change this)
18261 "preparedata" : true
18269 "click": this.onClick,
18270 "dblclick": this.onDblClick,
18271 "contextmenu": this.onContextMenu,
18275 this.selections = [];
18277 this.cmp = new Roo.CompositeElementLite([]);
18279 this.store = Roo.factory(this.store, Roo.data);
18280 this.setStore(this.store, true);
18283 if ( this.footer && this.footer.xtype) {
18285 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18287 this.footer.dataSource = this.store;
18288 this.footer.container = fctr;
18289 this.footer = Roo.factory(this.footer, Roo);
18290 fctr.insertFirst(this.el);
18292 // this is a bit insane - as the paging toolbar seems to detach the el..
18293 // dom.parentNode.parentNode.parentNode
18294 // they get detached?
18298 Roo.View.superclass.constructor.call(this);
18303 Roo.extend(Roo.View, Roo.util.Observable, {
18306 * @cfg {Roo.data.Store} store Data store to load data from.
18311 * @cfg {String|Roo.Element} el The container element.
18316 * @cfg {String|Roo.Template} tpl The template used by this View
18320 * @cfg {String} dataName the named area of the template to use as the data area
18321 * Works with domtemplates roo-name="name"
18325 * @cfg {String} selectedClass The css class to add to selected nodes
18327 selectedClass : "x-view-selected",
18329 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18334 * @cfg {String} text to display on mask (default Loading)
18338 * @cfg {Boolean} multiSelect Allow multiple selection
18340 multiSelect : false,
18342 * @cfg {Boolean} singleSelect Allow single selection
18344 singleSelect: false,
18347 * @cfg {Boolean} toggleSelect - selecting
18349 toggleSelect : false,
18352 * @cfg {Boolean} tickable - selecting
18357 * Returns the element this view is bound to.
18358 * @return {Roo.Element}
18360 getEl : function(){
18361 return this.wrapEl;
18367 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18369 refresh : function(){
18370 //Roo.log('refresh');
18373 // if we are using something like 'domtemplate', then
18374 // the what gets used is:
18375 // t.applySubtemplate(NAME, data, wrapping data..)
18376 // the outer template then get' applied with
18377 // the store 'extra data'
18378 // and the body get's added to the
18379 // roo-name="data" node?
18380 // <span class='roo-tpl-{name}'></span> ?????
18384 this.clearSelections();
18385 this.el.update("");
18387 var records = this.store.getRange();
18388 if(records.length < 1) {
18390 // is this valid?? = should it render a template??
18392 this.el.update(this.emptyText);
18396 if (this.dataName) {
18397 this.el.update(t.apply(this.store.meta)); //????
18398 el = this.el.child('.roo-tpl-' + this.dataName);
18401 for(var i = 0, len = records.length; i < len; i++){
18402 var data = this.prepareData(records[i].data, i, records[i]);
18403 this.fireEvent("preparedata", this, data, i, records[i]);
18405 var d = Roo.apply({}, data);
18408 Roo.apply(d, {'roo-id' : Roo.id()});
18412 Roo.each(this.parent.item, function(item){
18413 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18416 Roo.apply(d, {'roo-data-checked' : 'checked'});
18420 html[html.length] = Roo.util.Format.trim(
18422 t.applySubtemplate(this.dataName, d, this.store.meta) :
18429 el.update(html.join(""));
18430 this.nodes = el.dom.childNodes;
18431 this.updateIndexes(0);
18436 * Function to override to reformat the data that is sent to
18437 * the template for each node.
18438 * DEPRICATED - use the preparedata event handler.
18439 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18440 * a JSON object for an UpdateManager bound view).
18442 prepareData : function(data, index, record)
18444 this.fireEvent("preparedata", this, data, index, record);
18448 onUpdate : function(ds, record){
18449 // Roo.log('on update');
18450 this.clearSelections();
18451 var index = this.store.indexOf(record);
18452 var n = this.nodes[index];
18453 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18454 n.parentNode.removeChild(n);
18455 this.updateIndexes(index, index);
18461 onAdd : function(ds, records, index)
18463 //Roo.log(['on Add', ds, records, index] );
18464 this.clearSelections();
18465 if(this.nodes.length == 0){
18469 var n = this.nodes[index];
18470 for(var i = 0, len = records.length; i < len; i++){
18471 var d = this.prepareData(records[i].data, i, records[i]);
18473 this.tpl.insertBefore(n, d);
18476 this.tpl.append(this.el, d);
18479 this.updateIndexes(index);
18482 onRemove : function(ds, record, index){
18483 // Roo.log('onRemove');
18484 this.clearSelections();
18485 var el = this.dataName ?
18486 this.el.child('.roo-tpl-' + this.dataName) :
18489 el.dom.removeChild(this.nodes[index]);
18490 this.updateIndexes(index);
18494 * Refresh an individual node.
18495 * @param {Number} index
18497 refreshNode : function(index){
18498 this.onUpdate(this.store, this.store.getAt(index));
18501 updateIndexes : function(startIndex, endIndex){
18502 var ns = this.nodes;
18503 startIndex = startIndex || 0;
18504 endIndex = endIndex || ns.length - 1;
18505 for(var i = startIndex; i <= endIndex; i++){
18506 ns[i].nodeIndex = i;
18511 * Changes the data store this view uses and refresh the view.
18512 * @param {Store} store
18514 setStore : function(store, initial){
18515 if(!initial && this.store){
18516 this.store.un("datachanged", this.refresh);
18517 this.store.un("add", this.onAdd);
18518 this.store.un("remove", this.onRemove);
18519 this.store.un("update", this.onUpdate);
18520 this.store.un("clear", this.refresh);
18521 this.store.un("beforeload", this.onBeforeLoad);
18522 this.store.un("load", this.onLoad);
18523 this.store.un("loadexception", this.onLoad);
18527 store.on("datachanged", this.refresh, this);
18528 store.on("add", this.onAdd, this);
18529 store.on("remove", this.onRemove, this);
18530 store.on("update", this.onUpdate, this);
18531 store.on("clear", this.refresh, this);
18532 store.on("beforeload", this.onBeforeLoad, this);
18533 store.on("load", this.onLoad, this);
18534 store.on("loadexception", this.onLoad, this);
18542 * onbeforeLoad - masks the loading area.
18545 onBeforeLoad : function(store,opts)
18547 //Roo.log('onBeforeLoad');
18549 this.el.update("");
18551 this.el.mask(this.mask ? this.mask : "Loading" );
18553 onLoad : function ()
18560 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18561 * @param {HTMLElement} node
18562 * @return {HTMLElement} The template node
18564 findItemFromChild : function(node){
18565 var el = this.dataName ?
18566 this.el.child('.roo-tpl-' + this.dataName,true) :
18569 if(!node || node.parentNode == el){
18572 var p = node.parentNode;
18573 while(p && p != el){
18574 if(p.parentNode == el){
18583 onClick : function(e){
18584 var item = this.findItemFromChild(e.getTarget());
18586 var index = this.indexOf(item);
18587 if(this.onItemClick(item, index, e) !== false){
18588 this.fireEvent("click", this, index, item, e);
18591 this.clearSelections();
18596 onContextMenu : function(e){
18597 var item = this.findItemFromChild(e.getTarget());
18599 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18604 onDblClick : function(e){
18605 var item = this.findItemFromChild(e.getTarget());
18607 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18611 onItemClick : function(item, index, e)
18613 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18616 if (this.toggleSelect) {
18617 var m = this.isSelected(item) ? 'unselect' : 'select';
18620 _t[m](item, true, false);
18623 if(this.multiSelect || this.singleSelect){
18624 if(this.multiSelect && e.shiftKey && this.lastSelection){
18625 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18627 this.select(item, this.multiSelect && e.ctrlKey);
18628 this.lastSelection = item;
18631 if(!this.tickable){
18632 e.preventDefault();
18640 * Get the number of selected nodes.
18643 getSelectionCount : function(){
18644 return this.selections.length;
18648 * Get the currently selected nodes.
18649 * @return {Array} An array of HTMLElements
18651 getSelectedNodes : function(){
18652 return this.selections;
18656 * Get the indexes of the selected nodes.
18659 getSelectedIndexes : function(){
18660 var indexes = [], s = this.selections;
18661 for(var i = 0, len = s.length; i < len; i++){
18662 indexes.push(s[i].nodeIndex);
18668 * Clear all selections
18669 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18671 clearSelections : function(suppressEvent){
18672 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18673 this.cmp.elements = this.selections;
18674 this.cmp.removeClass(this.selectedClass);
18675 this.selections = [];
18676 if(!suppressEvent){
18677 this.fireEvent("selectionchange", this, this.selections);
18683 * Returns true if the passed node is selected
18684 * @param {HTMLElement/Number} node The node or node index
18685 * @return {Boolean}
18687 isSelected : function(node){
18688 var s = this.selections;
18692 node = this.getNode(node);
18693 return s.indexOf(node) !== -1;
18698 * @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
18699 * @param {Boolean} keepExisting (optional) true to keep existing selections
18700 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18702 select : function(nodeInfo, keepExisting, suppressEvent){
18703 if(nodeInfo instanceof Array){
18705 this.clearSelections(true);
18707 for(var i = 0, len = nodeInfo.length; i < len; i++){
18708 this.select(nodeInfo[i], true, true);
18712 var node = this.getNode(nodeInfo);
18713 if(!node || this.isSelected(node)){
18714 return; // already selected.
18717 this.clearSelections(true);
18720 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18721 Roo.fly(node).addClass(this.selectedClass);
18722 this.selections.push(node);
18723 if(!suppressEvent){
18724 this.fireEvent("selectionchange", this, this.selections);
18732 * @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
18733 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18734 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18736 unselect : function(nodeInfo, keepExisting, suppressEvent)
18738 if(nodeInfo instanceof Array){
18739 Roo.each(this.selections, function(s) {
18740 this.unselect(s, nodeInfo);
18744 var node = this.getNode(nodeInfo);
18745 if(!node || !this.isSelected(node)){
18746 //Roo.log("not selected");
18747 return; // not selected.
18751 Roo.each(this.selections, function(s) {
18753 Roo.fly(node).removeClass(this.selectedClass);
18760 this.selections= ns;
18761 this.fireEvent("selectionchange", this, this.selections);
18765 * Gets a template node.
18766 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18767 * @return {HTMLElement} The node or null if it wasn't found
18769 getNode : function(nodeInfo){
18770 if(typeof nodeInfo == "string"){
18771 return document.getElementById(nodeInfo);
18772 }else if(typeof nodeInfo == "number"){
18773 return this.nodes[nodeInfo];
18779 * Gets a range template nodes.
18780 * @param {Number} startIndex
18781 * @param {Number} endIndex
18782 * @return {Array} An array of nodes
18784 getNodes : function(start, end){
18785 var ns = this.nodes;
18786 start = start || 0;
18787 end = typeof end == "undefined" ? ns.length - 1 : end;
18790 for(var i = start; i <= end; i++){
18794 for(var i = start; i >= end; i--){
18802 * Finds the index of the passed node
18803 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18804 * @return {Number} The index of the node or -1
18806 indexOf : function(node){
18807 node = this.getNode(node);
18808 if(typeof node.nodeIndex == "number"){
18809 return node.nodeIndex;
18811 var ns = this.nodes;
18812 for(var i = 0, len = ns.length; i < len; i++){
18823 * based on jquery fullcalendar
18827 Roo.bootstrap = Roo.bootstrap || {};
18829 * @class Roo.bootstrap.Calendar
18830 * @extends Roo.bootstrap.Component
18831 * Bootstrap Calendar class
18832 * @cfg {Boolean} loadMask (true|false) default false
18833 * @cfg {Object} header generate the user specific header of the calendar, default false
18836 * Create a new Container
18837 * @param {Object} config The config object
18842 Roo.bootstrap.Calendar = function(config){
18843 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18847 * Fires when a date is selected
18848 * @param {DatePicker} this
18849 * @param {Date} date The selected date
18853 * @event monthchange
18854 * Fires when the displayed month changes
18855 * @param {DatePicker} this
18856 * @param {Date} date The selected month
18858 'monthchange': true,
18860 * @event evententer
18861 * Fires when mouse over an event
18862 * @param {Calendar} this
18863 * @param {event} Event
18865 'evententer': true,
18867 * @event eventleave
18868 * Fires when the mouse leaves an
18869 * @param {Calendar} this
18872 'eventleave': true,
18874 * @event eventclick
18875 * Fires when the mouse click an
18876 * @param {Calendar} this
18885 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
18888 * @cfg {Number} startDay
18889 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18897 getAutoCreate : function(){
18900 var fc_button = function(name, corner, style, content ) {
18901 return Roo.apply({},{
18903 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
18905 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18908 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18919 style : 'width:100%',
18926 cls : 'fc-header-left',
18928 fc_button('prev', 'left', 'arrow', '‹' ),
18929 fc_button('next', 'right', 'arrow', '›' ),
18930 { tag: 'span', cls: 'fc-header-space' },
18931 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
18939 cls : 'fc-header-center',
18943 cls: 'fc-header-title',
18946 html : 'month / year'
18954 cls : 'fc-header-right',
18956 /* fc_button('month', 'left', '', 'month' ),
18957 fc_button('week', '', '', 'week' ),
18958 fc_button('day', 'right', '', 'day' )
18970 header = this.header;
18973 var cal_heads = function() {
18975 // fixme - handle this.
18977 for (var i =0; i < Date.dayNames.length; i++) {
18978 var d = Date.dayNames[i];
18981 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18982 html : d.substring(0,3)
18986 ret[0].cls += ' fc-first';
18987 ret[6].cls += ' fc-last';
18990 var cal_cell = function(n) {
18993 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18998 cls: 'fc-day-number',
19002 cls: 'fc-day-content',
19006 style: 'position: relative;' // height: 17px;
19018 var cal_rows = function() {
19021 for (var r = 0; r < 6; r++) {
19028 for (var i =0; i < Date.dayNames.length; i++) {
19029 var d = Date.dayNames[i];
19030 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19033 row.cn[0].cls+=' fc-first';
19034 row.cn[0].cn[0].style = 'min-height:90px';
19035 row.cn[6].cls+=' fc-last';
19039 ret[0].cls += ' fc-first';
19040 ret[4].cls += ' fc-prev-last';
19041 ret[5].cls += ' fc-last';
19048 cls: 'fc-border-separate',
19049 style : 'width:100%',
19057 cls : 'fc-first fc-last',
19075 cls : 'fc-content',
19076 style : "position: relative;",
19079 cls : 'fc-view fc-view-month fc-grid',
19080 style : 'position: relative',
19081 unselectable : 'on',
19084 cls : 'fc-event-container',
19085 style : 'position:absolute;z-index:8;top:0;left:0;'
19103 initEvents : function()
19106 throw "can not find store for calendar";
19112 style: "text-align:center",
19116 style: "background-color:white;width:50%;margin:250 auto",
19120 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19131 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19133 var size = this.el.select('.fc-content', true).first().getSize();
19134 this.maskEl.setSize(size.width, size.height);
19135 this.maskEl.enableDisplayMode("block");
19136 if(!this.loadMask){
19137 this.maskEl.hide();
19140 this.store = Roo.factory(this.store, Roo.data);
19141 this.store.on('load', this.onLoad, this);
19142 this.store.on('beforeload', this.onBeforeLoad, this);
19146 this.cells = this.el.select('.fc-day',true);
19147 //Roo.log(this.cells);
19148 this.textNodes = this.el.query('.fc-day-number');
19149 this.cells.addClassOnOver('fc-state-hover');
19151 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19152 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19153 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19154 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19156 this.on('monthchange', this.onMonthChange, this);
19158 this.update(new Date().clearTime());
19161 resize : function() {
19162 var sz = this.el.getSize();
19164 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19165 this.el.select('.fc-day-content div',true).setHeight(34);
19170 showPrevMonth : function(e){
19171 this.update(this.activeDate.add("mo", -1));
19173 showToday : function(e){
19174 this.update(new Date().clearTime());
19177 showNextMonth : function(e){
19178 this.update(this.activeDate.add("mo", 1));
19182 showPrevYear : function(){
19183 this.update(this.activeDate.add("y", -1));
19187 showNextYear : function(){
19188 this.update(this.activeDate.add("y", 1));
19193 update : function(date)
19195 var vd = this.activeDate;
19196 this.activeDate = date;
19197 // if(vd && this.el){
19198 // var t = date.getTime();
19199 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19200 // Roo.log('using add remove');
19202 // this.fireEvent('monthchange', this, date);
19204 // this.cells.removeClass("fc-state-highlight");
19205 // this.cells.each(function(c){
19206 // if(c.dateValue == t){
19207 // c.addClass("fc-state-highlight");
19208 // setTimeout(function(){
19209 // try{c.dom.firstChild.focus();}catch(e){}
19219 var days = date.getDaysInMonth();
19221 var firstOfMonth = date.getFirstDateOfMonth();
19222 var startingPos = firstOfMonth.getDay()-this.startDay;
19224 if(startingPos < this.startDay){
19228 var pm = date.add(Date.MONTH, -1);
19229 var prevStart = pm.getDaysInMonth()-startingPos;
19231 this.cells = this.el.select('.fc-day',true);
19232 this.textNodes = this.el.query('.fc-day-number');
19233 this.cells.addClassOnOver('fc-state-hover');
19235 var cells = this.cells.elements;
19236 var textEls = this.textNodes;
19238 Roo.each(cells, function(cell){
19239 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19242 days += startingPos;
19244 // convert everything to numbers so it's fast
19245 var day = 86400000;
19246 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19249 //Roo.log(prevStart);
19251 var today = new Date().clearTime().getTime();
19252 var sel = date.clearTime().getTime();
19253 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19254 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19255 var ddMatch = this.disabledDatesRE;
19256 var ddText = this.disabledDatesText;
19257 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19258 var ddaysText = this.disabledDaysText;
19259 var format = this.format;
19261 var setCellClass = function(cal, cell){
19265 //Roo.log('set Cell Class');
19267 var t = d.getTime();
19271 cell.dateValue = t;
19273 cell.className += " fc-today";
19274 cell.className += " fc-state-highlight";
19275 cell.title = cal.todayText;
19278 // disable highlight in other month..
19279 //cell.className += " fc-state-highlight";
19284 cell.className = " fc-state-disabled";
19285 cell.title = cal.minText;
19289 cell.className = " fc-state-disabled";
19290 cell.title = cal.maxText;
19294 if(ddays.indexOf(d.getDay()) != -1){
19295 cell.title = ddaysText;
19296 cell.className = " fc-state-disabled";
19299 if(ddMatch && format){
19300 var fvalue = d.dateFormat(format);
19301 if(ddMatch.test(fvalue)){
19302 cell.title = ddText.replace("%0", fvalue);
19303 cell.className = " fc-state-disabled";
19307 if (!cell.initialClassName) {
19308 cell.initialClassName = cell.dom.className;
19311 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19316 for(; i < startingPos; i++) {
19317 textEls[i].innerHTML = (++prevStart);
19318 d.setDate(d.getDate()+1);
19320 cells[i].className = "fc-past fc-other-month";
19321 setCellClass(this, cells[i]);
19326 for(; i < days; i++){
19327 intDay = i - startingPos + 1;
19328 textEls[i].innerHTML = (intDay);
19329 d.setDate(d.getDate()+1);
19331 cells[i].className = ''; // "x-date-active";
19332 setCellClass(this, cells[i]);
19336 for(; i < 42; i++) {
19337 textEls[i].innerHTML = (++extraDays);
19338 d.setDate(d.getDate()+1);
19340 cells[i].className = "fc-future fc-other-month";
19341 setCellClass(this, cells[i]);
19344 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19346 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19348 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19349 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19351 if(totalRows != 6){
19352 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19353 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19356 this.fireEvent('monthchange', this, date);
19360 if(!this.internalRender){
19361 var main = this.el.dom.firstChild;
19362 var w = main.offsetWidth;
19363 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19364 Roo.fly(main).setWidth(w);
19365 this.internalRender = true;
19366 // opera does not respect the auto grow header center column
19367 // then, after it gets a width opera refuses to recalculate
19368 // without a second pass
19369 if(Roo.isOpera && !this.secondPass){
19370 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19371 this.secondPass = true;
19372 this.update.defer(10, this, [date]);
19379 findCell : function(dt) {
19380 dt = dt.clearTime().getTime();
19382 this.cells.each(function(c){
19383 //Roo.log("check " +c.dateValue + '?=' + dt);
19384 if(c.dateValue == dt){
19394 findCells : function(ev) {
19395 var s = ev.start.clone().clearTime().getTime();
19397 var e= ev.end.clone().clearTime().getTime();
19400 this.cells.each(function(c){
19401 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19403 if(c.dateValue > e){
19406 if(c.dateValue < s){
19415 // findBestRow: function(cells)
19419 // for (var i =0 ; i < cells.length;i++) {
19420 // ret = Math.max(cells[i].rows || 0,ret);
19427 addItem : function(ev)
19429 // look for vertical location slot in
19430 var cells = this.findCells(ev);
19432 // ev.row = this.findBestRow(cells);
19434 // work out the location.
19438 for(var i =0; i < cells.length; i++) {
19440 cells[i].row = cells[0].row;
19443 cells[i].row = cells[i].row + 1;
19453 if (crow.start.getY() == cells[i].getY()) {
19455 crow.end = cells[i];
19472 cells[0].events.push(ev);
19474 this.calevents.push(ev);
19477 clearEvents: function() {
19479 if(!this.calevents){
19483 Roo.each(this.cells.elements, function(c){
19489 Roo.each(this.calevents, function(e) {
19490 Roo.each(e.els, function(el) {
19491 el.un('mouseenter' ,this.onEventEnter, this);
19492 el.un('mouseleave' ,this.onEventLeave, this);
19497 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19503 renderEvents: function()
19507 this.cells.each(function(c) {
19516 if(c.row != c.events.length){
19517 r = 4 - (4 - (c.row - c.events.length));
19520 c.events = ev.slice(0, r);
19521 c.more = ev.slice(r);
19523 if(c.more.length && c.more.length == 1){
19524 c.events.push(c.more.pop());
19527 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19531 this.cells.each(function(c) {
19533 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19536 for (var e = 0; e < c.events.length; e++){
19537 var ev = c.events[e];
19538 var rows = ev.rows;
19540 for(var i = 0; i < rows.length; i++) {
19542 // how many rows should it span..
19545 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19546 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19548 unselectable : "on",
19551 cls: 'fc-event-inner',
19555 // cls: 'fc-event-time',
19556 // html : cells.length > 1 ? '' : ev.time
19560 cls: 'fc-event-title',
19561 html : String.format('{0}', ev.title)
19568 cls: 'ui-resizable-handle ui-resizable-e',
19569 html : '  '
19576 cfg.cls += ' fc-event-start';
19578 if ((i+1) == rows.length) {
19579 cfg.cls += ' fc-event-end';
19582 var ctr = _this.el.select('.fc-event-container',true).first();
19583 var cg = ctr.createChild(cfg);
19585 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19586 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19588 var r = (c.more.length) ? 1 : 0;
19589 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19590 cg.setWidth(ebox.right - sbox.x -2);
19592 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19593 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19594 cg.on('click', _this.onEventClick, _this, ev);
19605 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19606 style : 'position: absolute',
19607 unselectable : "on",
19610 cls: 'fc-event-inner',
19614 cls: 'fc-event-title',
19622 cls: 'ui-resizable-handle ui-resizable-e',
19623 html : '  '
19629 var ctr = _this.el.select('.fc-event-container',true).first();
19630 var cg = ctr.createChild(cfg);
19632 var sbox = c.select('.fc-day-content',true).first().getBox();
19633 var ebox = c.select('.fc-day-content',true).first().getBox();
19635 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19636 cg.setWidth(ebox.right - sbox.x -2);
19638 cg.on('click', _this.onMoreEventClick, _this, c.more);
19648 onEventEnter: function (e, el,event,d) {
19649 this.fireEvent('evententer', this, el, event);
19652 onEventLeave: function (e, el,event,d) {
19653 this.fireEvent('eventleave', this, el, event);
19656 onEventClick: function (e, el,event,d) {
19657 this.fireEvent('eventclick', this, el, event);
19660 onMonthChange: function () {
19664 onMoreEventClick: function(e, el, more)
19668 this.calpopover.placement = 'right';
19669 this.calpopover.setTitle('More');
19671 this.calpopover.setContent('');
19673 var ctr = this.calpopover.el.select('.popover-content', true).first();
19675 Roo.each(more, function(m){
19677 cls : 'fc-event-hori fc-event-draggable',
19680 var cg = ctr.createChild(cfg);
19682 cg.on('click', _this.onEventClick, _this, m);
19685 this.calpopover.show(el);
19690 onLoad: function ()
19692 this.calevents = [];
19695 if(this.store.getCount() > 0){
19696 this.store.data.each(function(d){
19699 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19700 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19701 time : d.data.start_time,
19702 title : d.data.title,
19703 description : d.data.description,
19704 venue : d.data.venue
19709 this.renderEvents();
19711 if(this.calevents.length && this.loadMask){
19712 this.maskEl.hide();
19716 onBeforeLoad: function()
19718 this.clearEvents();
19720 this.maskEl.show();
19734 * @class Roo.bootstrap.Popover
19735 * @extends Roo.bootstrap.Component
19736 * Bootstrap Popover class
19737 * @cfg {String} html contents of the popover (or false to use children..)
19738 * @cfg {String} title of popover (or false to hide)
19739 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19740 * @cfg {String} trigger click || hover (or false to trigger manually)
19741 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19742 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19743 * - if false and it has a 'parent' then it will be automatically added to that element
19744 * - if string - Roo.get will be called
19745 * @cfg {Number} delay - delay before showing
19748 * Create a new Popover
19749 * @param {Object} config The config object
19752 Roo.bootstrap.Popover = function(config){
19753 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19759 * After the popover show
19761 * @param {Roo.bootstrap.Popover} this
19766 * After the popover hide
19768 * @param {Roo.bootstrap.Popover} this
19774 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
19779 placement : 'right',
19780 trigger : 'hover', // hover
19786 can_build_overlaid : false,
19788 maskEl : false, // the mask element
19791 alignEl : false, // when show is called with an element - this get's stored.
19793 getChildContainer : function()
19795 return this.contentEl;
19798 getPopoverHeader : function()
19800 this.title = true; // flag not to hide it..
19801 this.headerEl.addClass('p-0');
19802 return this.headerEl
19806 getAutoCreate : function(){
19809 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19810 style: 'display:block',
19816 cls : 'popover-inner ',
19820 cls: 'popover-title popover-header',
19821 html : this.title === false ? '' : this.title
19824 cls : 'popover-content popover-body ' + (this.cls || ''),
19825 html : this.html || ''
19836 * @param {string} the title
19838 setTitle: function(str)
19842 this.headerEl.dom.innerHTML = str;
19847 * @param {string} the body content
19849 setContent: function(str)
19852 if (this.contentEl) {
19853 this.contentEl.dom.innerHTML = str;
19857 // as it get's added to the bottom of the page.
19858 onRender : function(ct, position)
19860 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19865 var cfg = Roo.apply({}, this.getAutoCreate());
19869 cfg.cls += ' ' + this.cls;
19872 cfg.style = this.style;
19874 //Roo.log("adding to ");
19875 this.el = Roo.get(document.body).createChild(cfg, position);
19876 // Roo.log(this.el);
19879 this.contentEl = this.el.select('.popover-content',true).first();
19880 this.headerEl = this.el.select('.popover-title',true).first();
19883 if(typeof(this.items) != 'undefined'){
19884 var items = this.items;
19887 for(var i =0;i < items.length;i++) {
19888 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19892 this.items = nitems;
19894 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19895 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19902 resizeMask : function()
19904 this.maskEl.setSize(
19905 Roo.lib.Dom.getViewWidth(true),
19906 Roo.lib.Dom.getViewHeight(true)
19910 initEvents : function()
19914 Roo.bootstrap.Popover.register(this);
19917 this.arrowEl = this.el.select('.arrow',true).first();
19918 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19919 this.el.enableDisplayMode('block');
19923 if (this.over === false && !this.parent()) {
19926 if (this.triggers === false) {
19931 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19932 var triggers = this.trigger ? this.trigger.split(' ') : [];
19933 Roo.each(triggers, function(trigger) {
19935 if (trigger == 'click') {
19936 on_el.on('click', this.toggle, this);
19937 } else if (trigger != 'manual') {
19938 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
19939 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19941 on_el.on(eventIn ,this.enter, this);
19942 on_el.on(eventOut, this.leave, this);
19952 toggle : function () {
19953 this.hoverState == 'in' ? this.leave() : this.enter();
19956 enter : function () {
19958 clearTimeout(this.timeout);
19960 this.hoverState = 'in';
19962 if (!this.delay || !this.delay.show) {
19967 this.timeout = setTimeout(function () {
19968 if (_t.hoverState == 'in') {
19971 }, this.delay.show)
19974 leave : function() {
19975 clearTimeout(this.timeout);
19977 this.hoverState = 'out';
19979 if (!this.delay || !this.delay.hide) {
19984 this.timeout = setTimeout(function () {
19985 if (_t.hoverState == 'out') {
19988 }, this.delay.hide)
19992 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19993 * @param {string} (left|right|top|bottom) position
19995 show : function (on_el, placement)
19997 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
19998 on_el = on_el || false; // default to false
20001 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20002 on_el = this.parent().el;
20003 } else if (this.over) {
20004 Roo.get(this.over);
20009 this.alignEl = Roo.get( on_el );
20012 this.render(document.body);
20018 if (this.title === false) {
20019 this.headerEl.hide();
20024 this.el.dom.style.display = 'block';
20027 if (this.alignEl) {
20028 this.updatePosition(this.placement, true);
20031 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20032 var es = this.el.getSize();
20033 var x = Roo.lib.Dom.getViewWidth()/2;
20034 var y = Roo.lib.Dom.getViewHeight()/2;
20035 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20040 //var arrow = this.el.select('.arrow',true).first();
20041 //arrow.set(align[2],
20043 this.el.addClass('in');
20047 this.hoverState = 'in';
20050 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20051 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20052 this.maskEl.dom.style.display = 'block';
20053 this.maskEl.addClass('show');
20055 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20057 this.fireEvent('show', this);
20061 * fire this manually after loading a grid in the table for example
20062 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20063 * @param {Boolean} try and move it if we cant get right position.
20065 updatePosition : function(placement, try_move)
20067 // allow for calling with no parameters
20068 placement = placement ? placement : this.placement;
20069 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20071 this.el.removeClass([
20072 'fade','top','bottom', 'left', 'right','in',
20073 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20075 this.el.addClass(placement + ' bs-popover-' + placement);
20077 if (!this.alignEl ) {
20081 switch (placement) {
20083 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20084 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20085 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20086 //normal display... or moved up/down.
20087 this.el.setXY(offset);
20088 var xy = this.alignEl.getAnchorXY('tr', false);
20090 this.arrowEl.setXY(xy);
20093 // continue through...
20094 return this.updatePosition('left', false);
20098 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20099 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20100 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20101 //normal display... or moved up/down.
20102 this.el.setXY(offset);
20103 var xy = this.alignEl.getAnchorXY('tl', false);
20104 xy[0]-=10;xy[1]+=5; // << fix me
20105 this.arrowEl.setXY(xy);
20109 return this.updatePosition('right', false);
20112 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20113 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20114 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20115 //normal display... or moved up/down.
20116 this.el.setXY(offset);
20117 var xy = this.alignEl.getAnchorXY('t', false);
20118 xy[1]-=10; // << fix me
20119 this.arrowEl.setXY(xy);
20123 return this.updatePosition('bottom', false);
20126 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20127 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20128 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20129 //normal display... or moved up/down.
20130 this.el.setXY(offset);
20131 var xy = this.alignEl.getAnchorXY('b', false);
20132 xy[1]+=2; // << fix me
20133 this.arrowEl.setXY(xy);
20137 return this.updatePosition('top', false);
20148 this.el.setXY([0,0]);
20149 this.el.removeClass('in');
20151 this.hoverState = null;
20152 this.maskEl.hide(); // always..
20153 this.fireEvent('hide', this);
20159 Roo.apply(Roo.bootstrap.Popover, {
20162 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20163 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20164 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20165 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20170 clickHander : false,
20173 onMouseDown : function(e)
20175 if (!e.getTarget(".roo-popover")) {
20183 register : function(popup)
20185 if (!Roo.bootstrap.Popover.clickHandler) {
20186 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20188 // hide other popups.
20190 this.popups.push(popup);
20192 hideAll : function()
20194 this.popups.forEach(function(p) {
20202 * Card header - holder for the card header elements.
20207 * @class Roo.bootstrap.PopoverNav
20208 * @extends Roo.bootstrap.NavGroup
20209 * Bootstrap Popover header navigation class
20211 * Create a new Popover Header Navigation
20212 * @param {Object} config The config object
20215 Roo.bootstrap.PopoverNav = function(config){
20216 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20219 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20222 container_method : 'getPopoverHeader'
20240 * @class Roo.bootstrap.Progress
20241 * @extends Roo.bootstrap.Component
20242 * Bootstrap Progress class
20243 * @cfg {Boolean} striped striped of the progress bar
20244 * @cfg {Boolean} active animated of the progress bar
20248 * Create a new Progress
20249 * @param {Object} config The config object
20252 Roo.bootstrap.Progress = function(config){
20253 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20256 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20261 getAutoCreate : function(){
20269 cfg.cls += ' progress-striped';
20273 cfg.cls += ' active';
20292 * @class Roo.bootstrap.ProgressBar
20293 * @extends Roo.bootstrap.Component
20294 * Bootstrap ProgressBar class
20295 * @cfg {Number} aria_valuenow aria-value now
20296 * @cfg {Number} aria_valuemin aria-value min
20297 * @cfg {Number} aria_valuemax aria-value max
20298 * @cfg {String} label label for the progress bar
20299 * @cfg {String} panel (success | info | warning | danger )
20300 * @cfg {String} role role of the progress bar
20301 * @cfg {String} sr_only text
20305 * Create a new ProgressBar
20306 * @param {Object} config The config object
20309 Roo.bootstrap.ProgressBar = function(config){
20310 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20313 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20317 aria_valuemax : 100,
20323 getAutoCreate : function()
20328 cls: 'progress-bar',
20329 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20341 cfg.role = this.role;
20344 if(this.aria_valuenow){
20345 cfg['aria-valuenow'] = this.aria_valuenow;
20348 if(this.aria_valuemin){
20349 cfg['aria-valuemin'] = this.aria_valuemin;
20352 if(this.aria_valuemax){
20353 cfg['aria-valuemax'] = this.aria_valuemax;
20356 if(this.label && !this.sr_only){
20357 cfg.html = this.label;
20361 cfg.cls += ' progress-bar-' + this.panel;
20367 update : function(aria_valuenow)
20369 this.aria_valuenow = aria_valuenow;
20371 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20386 * @class Roo.bootstrap.TabGroup
20387 * @extends Roo.bootstrap.Column
20388 * Bootstrap Column class
20389 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20390 * @cfg {Boolean} carousel true to make the group behave like a carousel
20391 * @cfg {Boolean} bullets show bullets for the panels
20392 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20393 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20394 * @cfg {Boolean} showarrow (true|false) show arrow default true
20397 * Create a new TabGroup
20398 * @param {Object} config The config object
20401 Roo.bootstrap.TabGroup = function(config){
20402 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20404 this.navId = Roo.id();
20407 Roo.bootstrap.TabGroup.register(this);
20411 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20414 transition : false,
20419 slideOnTouch : false,
20422 getAutoCreate : function()
20424 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20426 cfg.cls += ' tab-content';
20428 if (this.carousel) {
20429 cfg.cls += ' carousel slide';
20432 cls : 'carousel-inner',
20436 if(this.bullets && !Roo.isTouch){
20439 cls : 'carousel-bullets',
20443 if(this.bullets_cls){
20444 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20451 cfg.cn[0].cn.push(bullets);
20454 if(this.showarrow){
20455 cfg.cn[0].cn.push({
20457 class : 'carousel-arrow',
20461 class : 'carousel-prev',
20465 class : 'fa fa-chevron-left'
20471 class : 'carousel-next',
20475 class : 'fa fa-chevron-right'
20488 initEvents: function()
20490 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20491 // this.el.on("touchstart", this.onTouchStart, this);
20494 if(this.autoslide){
20497 this.slideFn = window.setInterval(function() {
20498 _this.showPanelNext();
20502 if(this.showarrow){
20503 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20504 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20510 // onTouchStart : function(e, el, o)
20512 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20516 // this.showPanelNext();
20520 getChildContainer : function()
20522 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20526 * register a Navigation item
20527 * @param {Roo.bootstrap.NavItem} the navitem to add
20529 register : function(item)
20531 this.tabs.push( item);
20532 item.navId = this.navId; // not really needed..
20537 getActivePanel : function()
20540 Roo.each(this.tabs, function(t) {
20550 getPanelByName : function(n)
20553 Roo.each(this.tabs, function(t) {
20554 if (t.tabId == n) {
20562 indexOfPanel : function(p)
20565 Roo.each(this.tabs, function(t,i) {
20566 if (t.tabId == p.tabId) {
20575 * show a specific panel
20576 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20577 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20579 showPanel : function (pan)
20581 if(this.transition || typeof(pan) == 'undefined'){
20582 Roo.log("waiting for the transitionend");
20586 if (typeof(pan) == 'number') {
20587 pan = this.tabs[pan];
20590 if (typeof(pan) == 'string') {
20591 pan = this.getPanelByName(pan);
20594 var cur = this.getActivePanel();
20597 Roo.log('pan or acitve pan is undefined');
20601 if (pan.tabId == this.getActivePanel().tabId) {
20605 if (false === cur.fireEvent('beforedeactivate')) {
20609 if(this.bullets > 0 && !Roo.isTouch){
20610 this.setActiveBullet(this.indexOfPanel(pan));
20613 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20615 //class="carousel-item carousel-item-next carousel-item-left"
20617 this.transition = true;
20618 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20619 var lr = dir == 'next' ? 'left' : 'right';
20620 pan.el.addClass(dir); // or prev
20621 pan.el.addClass('carousel-item-' + dir); // or prev
20622 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20623 cur.el.addClass(lr); // or right
20624 pan.el.addClass(lr);
20625 cur.el.addClass('carousel-item-' +lr); // or right
20626 pan.el.addClass('carousel-item-' +lr);
20630 cur.el.on('transitionend', function() {
20631 Roo.log("trans end?");
20633 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20634 pan.setActive(true);
20636 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20637 cur.setActive(false);
20639 _this.transition = false;
20641 }, this, { single: true } );
20646 cur.setActive(false);
20647 pan.setActive(true);
20652 showPanelNext : function()
20654 var i = this.indexOfPanel(this.getActivePanel());
20656 if (i >= this.tabs.length - 1 && !this.autoslide) {
20660 if (i >= this.tabs.length - 1 && this.autoslide) {
20664 this.showPanel(this.tabs[i+1]);
20667 showPanelPrev : function()
20669 var i = this.indexOfPanel(this.getActivePanel());
20671 if (i < 1 && !this.autoslide) {
20675 if (i < 1 && this.autoslide) {
20676 i = this.tabs.length;
20679 this.showPanel(this.tabs[i-1]);
20683 addBullet: function()
20685 if(!this.bullets || Roo.isTouch){
20688 var ctr = this.el.select('.carousel-bullets',true).first();
20689 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20690 var bullet = ctr.createChild({
20691 cls : 'bullet bullet-' + i
20692 },ctr.dom.lastChild);
20697 bullet.on('click', (function(e, el, o, ii, t){
20699 e.preventDefault();
20701 this.showPanel(ii);
20703 if(this.autoslide && this.slideFn){
20704 clearInterval(this.slideFn);
20705 this.slideFn = window.setInterval(function() {
20706 _this.showPanelNext();
20710 }).createDelegate(this, [i, bullet], true));
20715 setActiveBullet : function(i)
20721 Roo.each(this.el.select('.bullet', true).elements, function(el){
20722 el.removeClass('selected');
20725 var bullet = this.el.select('.bullet-' + i, true).first();
20731 bullet.addClass('selected');
20742 Roo.apply(Roo.bootstrap.TabGroup, {
20746 * register a Navigation Group
20747 * @param {Roo.bootstrap.NavGroup} the navgroup to add
20749 register : function(navgrp)
20751 this.groups[navgrp.navId] = navgrp;
20755 * fetch a Navigation Group based on the navigation ID
20756 * if one does not exist , it will get created.
20757 * @param {string} the navgroup to add
20758 * @returns {Roo.bootstrap.NavGroup} the navgroup
20760 get: function(navId) {
20761 if (typeof(this.groups[navId]) == 'undefined') {
20762 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20764 return this.groups[navId] ;
20779 * @class Roo.bootstrap.TabPanel
20780 * @extends Roo.bootstrap.Component
20781 * Bootstrap TabPanel class
20782 * @cfg {Boolean} active panel active
20783 * @cfg {String} html panel content
20784 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20785 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20786 * @cfg {String} href click to link..
20787 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20791 * Create a new TabPanel
20792 * @param {Object} config The config object
20795 Roo.bootstrap.TabPanel = function(config){
20796 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20800 * Fires when the active status changes
20801 * @param {Roo.bootstrap.TabPanel} this
20802 * @param {Boolean} state the new state
20807 * @event beforedeactivate
20808 * Fires before a tab is de-activated - can be used to do validation on a form.
20809 * @param {Roo.bootstrap.TabPanel} this
20810 * @return {Boolean} false if there is an error
20813 'beforedeactivate': true
20816 this.tabId = this.tabId || Roo.id();
20820 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
20827 touchSlide : false,
20828 getAutoCreate : function(){
20833 // item is needed for carousel - not sure if it has any effect otherwise
20834 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20835 html: this.html || ''
20839 cfg.cls += ' active';
20843 cfg.tabId = this.tabId;
20851 initEvents: function()
20853 var p = this.parent();
20855 this.navId = this.navId || p.navId;
20857 if (typeof(this.navId) != 'undefined') {
20858 // not really needed.. but just in case.. parent should be a NavGroup.
20859 var tg = Roo.bootstrap.TabGroup.get(this.navId);
20863 var i = tg.tabs.length - 1;
20865 if(this.active && tg.bullets > 0 && i < tg.bullets){
20866 tg.setActiveBullet(i);
20870 this.el.on('click', this.onClick, this);
20872 if(Roo.isTouch && this.touchSlide){
20873 this.el.on("touchstart", this.onTouchStart, this);
20874 this.el.on("touchmove", this.onTouchMove, this);
20875 this.el.on("touchend", this.onTouchEnd, this);
20880 onRender : function(ct, position)
20882 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20885 setActive : function(state)
20887 Roo.log("panel - set active " + this.tabId + "=" + state);
20889 this.active = state;
20891 this.el.removeClass('active');
20893 } else if (!this.el.hasClass('active')) {
20894 this.el.addClass('active');
20897 this.fireEvent('changed', this, state);
20900 onClick : function(e)
20902 e.preventDefault();
20904 if(!this.href.length){
20908 window.location.href = this.href;
20917 onTouchStart : function(e)
20919 this.swiping = false;
20921 this.startX = e.browserEvent.touches[0].clientX;
20922 this.startY = e.browserEvent.touches[0].clientY;
20925 onTouchMove : function(e)
20927 this.swiping = true;
20929 this.endX = e.browserEvent.touches[0].clientX;
20930 this.endY = e.browserEvent.touches[0].clientY;
20933 onTouchEnd : function(e)
20940 var tabGroup = this.parent();
20942 if(this.endX > this.startX){ // swiping right
20943 tabGroup.showPanelPrev();
20947 if(this.startX > this.endX){ // swiping left
20948 tabGroup.showPanelNext();
20967 * @class Roo.bootstrap.DateField
20968 * @extends Roo.bootstrap.Input
20969 * Bootstrap DateField class
20970 * @cfg {Number} weekStart default 0
20971 * @cfg {String} viewMode default empty, (months|years)
20972 * @cfg {String} minViewMode default empty, (months|years)
20973 * @cfg {Number} startDate default -Infinity
20974 * @cfg {Number} endDate default Infinity
20975 * @cfg {Boolean} todayHighlight default false
20976 * @cfg {Boolean} todayBtn default false
20977 * @cfg {Boolean} calendarWeeks default false
20978 * @cfg {Object} daysOfWeekDisabled default empty
20979 * @cfg {Boolean} singleMode default false (true | false)
20981 * @cfg {Boolean} keyboardNavigation default true
20982 * @cfg {String} language default en
20985 * Create a new DateField
20986 * @param {Object} config The config object
20989 Roo.bootstrap.DateField = function(config){
20990 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20994 * Fires when this field show.
20995 * @param {Roo.bootstrap.DateField} this
20996 * @param {Mixed} date The date value
21001 * Fires when this field hide.
21002 * @param {Roo.bootstrap.DateField} this
21003 * @param {Mixed} date The date value
21008 * Fires when select a date.
21009 * @param {Roo.bootstrap.DateField} this
21010 * @param {Mixed} date The date value
21014 * @event beforeselect
21015 * Fires when before select a date.
21016 * @param {Roo.bootstrap.DateField} this
21017 * @param {Mixed} date The date value
21019 beforeselect : true
21023 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21026 * @cfg {String} format
21027 * The default date format string which can be overriden for localization support. The format must be
21028 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21032 * @cfg {String} altFormats
21033 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21034 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21036 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21044 todayHighlight : false,
21050 keyboardNavigation: true,
21052 calendarWeeks: false,
21054 startDate: -Infinity,
21058 daysOfWeekDisabled: [],
21062 singleMode : false,
21064 UTCDate: function()
21066 return new Date(Date.UTC.apply(Date, arguments));
21069 UTCToday: function()
21071 var today = new Date();
21072 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21075 getDate: function() {
21076 var d = this.getUTCDate();
21077 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21080 getUTCDate: function() {
21084 setDate: function(d) {
21085 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21088 setUTCDate: function(d) {
21090 this.setValue(this.formatDate(this.date));
21093 onRender: function(ct, position)
21096 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21098 this.language = this.language || 'en';
21099 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21100 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21102 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21103 this.format = this.format || 'm/d/y';
21104 this.isInline = false;
21105 this.isInput = true;
21106 this.component = this.el.select('.add-on', true).first() || false;
21107 this.component = (this.component && this.component.length === 0) ? false : this.component;
21108 this.hasInput = this.component && this.inputEl().length;
21110 if (typeof(this.minViewMode === 'string')) {
21111 switch (this.minViewMode) {
21113 this.minViewMode = 1;
21116 this.minViewMode = 2;
21119 this.minViewMode = 0;
21124 if (typeof(this.viewMode === 'string')) {
21125 switch (this.viewMode) {
21138 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21140 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21142 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21144 this.picker().on('mousedown', this.onMousedown, this);
21145 this.picker().on('click', this.onClick, this);
21147 this.picker().addClass('datepicker-dropdown');
21149 this.startViewMode = this.viewMode;
21151 if(this.singleMode){
21152 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21153 v.setVisibilityMode(Roo.Element.DISPLAY);
21157 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21158 v.setStyle('width', '189px');
21162 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21163 if(!this.calendarWeeks){
21168 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21169 v.attr('colspan', function(i, val){
21170 return parseInt(val) + 1;
21175 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21177 this.setStartDate(this.startDate);
21178 this.setEndDate(this.endDate);
21180 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21187 if(this.isInline) {
21192 picker : function()
21194 return this.pickerEl;
21195 // return this.el.select('.datepicker', true).first();
21198 fillDow: function()
21200 var dowCnt = this.weekStart;
21209 if(this.calendarWeeks){
21217 while (dowCnt < this.weekStart + 7) {
21221 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21225 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21228 fillMonths: function()
21231 var months = this.picker().select('>.datepicker-months td', true).first();
21233 months.dom.innerHTML = '';
21239 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21242 months.createChild(month);
21249 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;
21251 if (this.date < this.startDate) {
21252 this.viewDate = new Date(this.startDate);
21253 } else if (this.date > this.endDate) {
21254 this.viewDate = new Date(this.endDate);
21256 this.viewDate = new Date(this.date);
21264 var d = new Date(this.viewDate),
21265 year = d.getUTCFullYear(),
21266 month = d.getUTCMonth(),
21267 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21268 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21269 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21270 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21271 currentDate = this.date && this.date.valueOf(),
21272 today = this.UTCToday();
21274 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21276 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21278 // this.picker.select('>tfoot th.today').
21279 // .text(dates[this.language].today)
21280 // .toggle(this.todayBtn !== false);
21282 this.updateNavArrows();
21285 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21287 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21289 prevMonth.setUTCDate(day);
21291 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21293 var nextMonth = new Date(prevMonth);
21295 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21297 nextMonth = nextMonth.valueOf();
21299 var fillMonths = false;
21301 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21303 while(prevMonth.valueOf() <= nextMonth) {
21306 if (prevMonth.getUTCDay() === this.weekStart) {
21308 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21316 if(this.calendarWeeks){
21317 // ISO 8601: First week contains first thursday.
21318 // ISO also states week starts on Monday, but we can be more abstract here.
21320 // Start of current week: based on weekstart/current date
21321 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21322 // Thursday of this week
21323 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21324 // First Thursday of year, year from thursday
21325 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21326 // Calendar week: ms between thursdays, div ms per day, div 7 days
21327 calWeek = (th - yth) / 864e5 / 7 + 1;
21329 fillMonths.cn.push({
21337 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21339 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21342 if (this.todayHighlight &&
21343 prevMonth.getUTCFullYear() == today.getFullYear() &&
21344 prevMonth.getUTCMonth() == today.getMonth() &&
21345 prevMonth.getUTCDate() == today.getDate()) {
21346 clsName += ' today';
21349 if (currentDate && prevMonth.valueOf() === currentDate) {
21350 clsName += ' active';
21353 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21354 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21355 clsName += ' disabled';
21358 fillMonths.cn.push({
21360 cls: 'day ' + clsName,
21361 html: prevMonth.getDate()
21364 prevMonth.setDate(prevMonth.getDate()+1);
21367 var currentYear = this.date && this.date.getUTCFullYear();
21368 var currentMonth = this.date && this.date.getUTCMonth();
21370 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21372 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21373 v.removeClass('active');
21375 if(currentYear === year && k === currentMonth){
21376 v.addClass('active');
21379 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21380 v.addClass('disabled');
21386 year = parseInt(year/10, 10) * 10;
21388 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21390 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21393 for (var i = -1; i < 11; i++) {
21394 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21396 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21404 showMode: function(dir)
21407 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21410 Roo.each(this.picker().select('>div',true).elements, function(v){
21411 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21414 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21419 if(this.isInline) {
21423 this.picker().removeClass(['bottom', 'top']);
21425 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21427 * place to the top of element!
21431 this.picker().addClass('top');
21432 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21437 this.picker().addClass('bottom');
21439 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21442 parseDate : function(value)
21444 if(!value || value instanceof Date){
21447 var v = Date.parseDate(value, this.format);
21448 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21449 v = Date.parseDate(value, 'Y-m-d');
21451 if(!v && this.altFormats){
21452 if(!this.altFormatsArray){
21453 this.altFormatsArray = this.altFormats.split("|");
21455 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21456 v = Date.parseDate(value, this.altFormatsArray[i]);
21462 formatDate : function(date, fmt)
21464 return (!date || !(date instanceof Date)) ?
21465 date : date.dateFormat(fmt || this.format);
21468 onFocus : function()
21470 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21474 onBlur : function()
21476 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21478 var d = this.inputEl().getValue();
21485 showPopup : function()
21487 this.picker().show();
21491 this.fireEvent('showpopup', this, this.date);
21494 hidePopup : function()
21496 if(this.isInline) {
21499 this.picker().hide();
21500 this.viewMode = this.startViewMode;
21503 this.fireEvent('hidepopup', this, this.date);
21507 onMousedown: function(e)
21509 e.stopPropagation();
21510 e.preventDefault();
21515 Roo.bootstrap.DateField.superclass.keyup.call(this);
21519 setValue: function(v)
21521 if(this.fireEvent('beforeselect', this, v) !== false){
21522 var d = new Date(this.parseDate(v) ).clearTime();
21524 if(isNaN(d.getTime())){
21525 this.date = this.viewDate = '';
21526 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21530 v = this.formatDate(d);
21532 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21534 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21538 this.fireEvent('select', this, this.date);
21542 getValue: function()
21544 return this.formatDate(this.date);
21547 fireKey: function(e)
21549 if (!this.picker().isVisible()){
21550 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21556 var dateChanged = false,
21558 newDate, newViewDate;
21563 e.preventDefault();
21567 if (!this.keyboardNavigation) {
21570 dir = e.keyCode == 37 ? -1 : 1;
21573 newDate = this.moveYear(this.date, dir);
21574 newViewDate = this.moveYear(this.viewDate, dir);
21575 } else if (e.shiftKey){
21576 newDate = this.moveMonth(this.date, dir);
21577 newViewDate = this.moveMonth(this.viewDate, dir);
21579 newDate = new Date(this.date);
21580 newDate.setUTCDate(this.date.getUTCDate() + dir);
21581 newViewDate = new Date(this.viewDate);
21582 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21584 if (this.dateWithinRange(newDate)){
21585 this.date = newDate;
21586 this.viewDate = newViewDate;
21587 this.setValue(this.formatDate(this.date));
21589 e.preventDefault();
21590 dateChanged = true;
21595 if (!this.keyboardNavigation) {
21598 dir = e.keyCode == 38 ? -1 : 1;
21600 newDate = this.moveYear(this.date, dir);
21601 newViewDate = this.moveYear(this.viewDate, dir);
21602 } else if (e.shiftKey){
21603 newDate = this.moveMonth(this.date, dir);
21604 newViewDate = this.moveMonth(this.viewDate, dir);
21606 newDate = new Date(this.date);
21607 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21608 newViewDate = new Date(this.viewDate);
21609 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21611 if (this.dateWithinRange(newDate)){
21612 this.date = newDate;
21613 this.viewDate = newViewDate;
21614 this.setValue(this.formatDate(this.date));
21616 e.preventDefault();
21617 dateChanged = true;
21621 this.setValue(this.formatDate(this.date));
21623 e.preventDefault();
21626 this.setValue(this.formatDate(this.date));
21640 onClick: function(e)
21642 e.stopPropagation();
21643 e.preventDefault();
21645 var target = e.getTarget();
21647 if(target.nodeName.toLowerCase() === 'i'){
21648 target = Roo.get(target).dom.parentNode;
21651 var nodeName = target.nodeName;
21652 var className = target.className;
21653 var html = target.innerHTML;
21654 //Roo.log(nodeName);
21656 switch(nodeName.toLowerCase()) {
21658 switch(className) {
21664 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21665 switch(this.viewMode){
21667 this.viewDate = this.moveMonth(this.viewDate, dir);
21671 this.viewDate = this.moveYear(this.viewDate, dir);
21677 var date = new Date();
21678 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21680 this.setValue(this.formatDate(this.date));
21687 if (className.indexOf('disabled') < 0) {
21688 this.viewDate.setUTCDate(1);
21689 if (className.indexOf('month') > -1) {
21690 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21692 var year = parseInt(html, 10) || 0;
21693 this.viewDate.setUTCFullYear(year);
21697 if(this.singleMode){
21698 this.setValue(this.formatDate(this.viewDate));
21709 //Roo.log(className);
21710 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21711 var day = parseInt(html, 10) || 1;
21712 var year = (this.viewDate || new Date()).getUTCFullYear(),
21713 month = (this.viewDate || new Date()).getUTCMonth();
21715 if (className.indexOf('old') > -1) {
21722 } else if (className.indexOf('new') > -1) {
21730 //Roo.log([year,month,day]);
21731 this.date = this.UTCDate(year, month, day,0,0,0,0);
21732 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21734 //Roo.log(this.formatDate(this.date));
21735 this.setValue(this.formatDate(this.date));
21742 setStartDate: function(startDate)
21744 this.startDate = startDate || -Infinity;
21745 if (this.startDate !== -Infinity) {
21746 this.startDate = this.parseDate(this.startDate);
21749 this.updateNavArrows();
21752 setEndDate: function(endDate)
21754 this.endDate = endDate || Infinity;
21755 if (this.endDate !== Infinity) {
21756 this.endDate = this.parseDate(this.endDate);
21759 this.updateNavArrows();
21762 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21764 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21765 if (typeof(this.daysOfWeekDisabled) !== 'object') {
21766 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21768 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21769 return parseInt(d, 10);
21772 this.updateNavArrows();
21775 updateNavArrows: function()
21777 if(this.singleMode){
21781 var d = new Date(this.viewDate),
21782 year = d.getUTCFullYear(),
21783 month = d.getUTCMonth();
21785 Roo.each(this.picker().select('.prev', true).elements, function(v){
21787 switch (this.viewMode) {
21790 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21796 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21803 Roo.each(this.picker().select('.next', true).elements, function(v){
21805 switch (this.viewMode) {
21808 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21814 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21822 moveMonth: function(date, dir)
21827 var new_date = new Date(date.valueOf()),
21828 day = new_date.getUTCDate(),
21829 month = new_date.getUTCMonth(),
21830 mag = Math.abs(dir),
21832 dir = dir > 0 ? 1 : -1;
21835 // If going back one month, make sure month is not current month
21836 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21838 return new_date.getUTCMonth() == month;
21840 // If going forward one month, make sure month is as expected
21841 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21843 return new_date.getUTCMonth() != new_month;
21845 new_month = month + dir;
21846 new_date.setUTCMonth(new_month);
21847 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21848 if (new_month < 0 || new_month > 11) {
21849 new_month = (new_month + 12) % 12;
21852 // For magnitudes >1, move one month at a time...
21853 for (var i=0; i<mag; i++) {
21854 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21855 new_date = this.moveMonth(new_date, dir);
21857 // ...then reset the day, keeping it in the new month
21858 new_month = new_date.getUTCMonth();
21859 new_date.setUTCDate(day);
21861 return new_month != new_date.getUTCMonth();
21864 // Common date-resetting loop -- if date is beyond end of month, make it
21867 new_date.setUTCDate(--day);
21868 new_date.setUTCMonth(new_month);
21873 moveYear: function(date, dir)
21875 return this.moveMonth(date, dir*12);
21878 dateWithinRange: function(date)
21880 return date >= this.startDate && date <= this.endDate;
21886 this.picker().remove();
21889 validateValue : function(value)
21891 if(this.getVisibilityEl().hasClass('hidden')){
21895 if(value.length < 1) {
21896 if(this.allowBlank){
21902 if(value.length < this.minLength){
21905 if(value.length > this.maxLength){
21909 var vt = Roo.form.VTypes;
21910 if(!vt[this.vtype](value, this)){
21914 if(typeof this.validator == "function"){
21915 var msg = this.validator(value);
21921 if(this.regex && !this.regex.test(value)){
21925 if(typeof(this.parseDate(value)) == 'undefined'){
21929 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21933 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21943 this.date = this.viewDate = '';
21945 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21950 Roo.apply(Roo.bootstrap.DateField, {
21961 html: '<i class="fa fa-arrow-left"/>'
21971 html: '<i class="fa fa-arrow-right"/>'
22013 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22014 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22015 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22016 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22017 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22030 navFnc: 'FullYear',
22035 navFnc: 'FullYear',
22040 Roo.apply(Roo.bootstrap.DateField, {
22044 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22048 cls: 'datepicker-days',
22052 cls: 'table-condensed',
22054 Roo.bootstrap.DateField.head,
22058 Roo.bootstrap.DateField.footer
22065 cls: 'datepicker-months',
22069 cls: 'table-condensed',
22071 Roo.bootstrap.DateField.head,
22072 Roo.bootstrap.DateField.content,
22073 Roo.bootstrap.DateField.footer
22080 cls: 'datepicker-years',
22084 cls: 'table-condensed',
22086 Roo.bootstrap.DateField.head,
22087 Roo.bootstrap.DateField.content,
22088 Roo.bootstrap.DateField.footer
22107 * @class Roo.bootstrap.TimeField
22108 * @extends Roo.bootstrap.Input
22109 * Bootstrap DateField class
22113 * Create a new TimeField
22114 * @param {Object} config The config object
22117 Roo.bootstrap.TimeField = function(config){
22118 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22122 * Fires when this field show.
22123 * @param {Roo.bootstrap.DateField} thisthis
22124 * @param {Mixed} date The date value
22129 * Fires when this field hide.
22130 * @param {Roo.bootstrap.DateField} this
22131 * @param {Mixed} date The date value
22136 * Fires when select a date.
22137 * @param {Roo.bootstrap.DateField} this
22138 * @param {Mixed} date The date value
22144 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22147 * @cfg {String} format
22148 * The default time format string which can be overriden for localization support. The format must be
22149 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22153 getAutoCreate : function()
22155 this.after = '<i class="fa far fa-clock"></i>';
22156 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22160 onRender: function(ct, position)
22163 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22165 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22167 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22169 this.pop = this.picker().select('>.datepicker-time',true).first();
22170 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22172 this.picker().on('mousedown', this.onMousedown, this);
22173 this.picker().on('click', this.onClick, this);
22175 this.picker().addClass('datepicker-dropdown');
22180 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22181 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22182 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22183 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22184 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22185 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22189 fireKey: function(e){
22190 if (!this.picker().isVisible()){
22191 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22197 e.preventDefault();
22205 this.onTogglePeriod();
22208 this.onIncrementMinutes();
22211 this.onDecrementMinutes();
22220 onClick: function(e) {
22221 e.stopPropagation();
22222 e.preventDefault();
22225 picker : function()
22227 return this.pickerEl;
22230 fillTime: function()
22232 var time = this.pop.select('tbody', true).first();
22234 time.dom.innerHTML = '';
22249 cls: 'hours-up fa fas fa-chevron-up'
22269 cls: 'minutes-up fa fas fa-chevron-up'
22290 cls: 'timepicker-hour',
22305 cls: 'timepicker-minute',
22320 cls: 'btn btn-primary period',
22342 cls: 'hours-down fa fas fa-chevron-down'
22362 cls: 'minutes-down fa fas fa-chevron-down'
22380 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22387 var hours = this.time.getHours();
22388 var minutes = this.time.getMinutes();
22401 hours = hours - 12;
22405 hours = '0' + hours;
22409 minutes = '0' + minutes;
22412 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22413 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22414 this.pop.select('button', true).first().dom.innerHTML = period;
22420 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22422 var cls = ['bottom'];
22424 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22431 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22435 //this.picker().setXY(20000,20000);
22436 this.picker().addClass(cls.join('-'));
22440 Roo.each(cls, function(c){
22445 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22446 //_this.picker().setTop(_this.inputEl().getHeight());
22450 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22452 //_this.picker().setTop(0 - _this.picker().getHeight());
22457 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22461 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22469 onFocus : function()
22471 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22475 onBlur : function()
22477 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22483 this.picker().show();
22488 this.fireEvent('show', this, this.date);
22493 this.picker().hide();
22496 this.fireEvent('hide', this, this.date);
22499 setTime : function()
22502 this.setValue(this.time.format(this.format));
22504 this.fireEvent('select', this, this.date);
22509 onMousedown: function(e){
22510 e.stopPropagation();
22511 e.preventDefault();
22514 onIncrementHours: function()
22516 Roo.log('onIncrementHours');
22517 this.time = this.time.add(Date.HOUR, 1);
22522 onDecrementHours: function()
22524 Roo.log('onDecrementHours');
22525 this.time = this.time.add(Date.HOUR, -1);
22529 onIncrementMinutes: function()
22531 Roo.log('onIncrementMinutes');
22532 this.time = this.time.add(Date.MINUTE, 1);
22536 onDecrementMinutes: function()
22538 Roo.log('onDecrementMinutes');
22539 this.time = this.time.add(Date.MINUTE, -1);
22543 onTogglePeriod: function()
22545 Roo.log('onTogglePeriod');
22546 this.time = this.time.add(Date.HOUR, 12);
22554 Roo.apply(Roo.bootstrap.TimeField, {
22558 cls: 'datepicker dropdown-menu',
22562 cls: 'datepicker-time',
22566 cls: 'table-condensed',
22595 cls: 'btn btn-info ok',
22623 * @class Roo.bootstrap.MonthField
22624 * @extends Roo.bootstrap.Input
22625 * Bootstrap MonthField class
22627 * @cfg {String} language default en
22630 * Create a new MonthField
22631 * @param {Object} config The config object
22634 Roo.bootstrap.MonthField = function(config){
22635 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22640 * Fires when this field show.
22641 * @param {Roo.bootstrap.MonthField} this
22642 * @param {Mixed} date The date value
22647 * Fires when this field hide.
22648 * @param {Roo.bootstrap.MonthField} this
22649 * @param {Mixed} date The date value
22654 * Fires when select a date.
22655 * @param {Roo.bootstrap.MonthField} this
22656 * @param {String} oldvalue The old value
22657 * @param {String} newvalue The new value
22663 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22665 onRender: function(ct, position)
22668 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22670 this.language = this.language || 'en';
22671 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22672 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22674 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22675 this.isInline = false;
22676 this.isInput = true;
22677 this.component = this.el.select('.add-on', true).first() || false;
22678 this.component = (this.component && this.component.length === 0) ? false : this.component;
22679 this.hasInput = this.component && this.inputEL().length;
22681 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22683 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22685 this.picker().on('mousedown', this.onMousedown, this);
22686 this.picker().on('click', this.onClick, this);
22688 this.picker().addClass('datepicker-dropdown');
22690 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22691 v.setStyle('width', '189px');
22698 if(this.isInline) {
22704 setValue: function(v, suppressEvent)
22706 var o = this.getValue();
22708 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22712 if(suppressEvent !== true){
22713 this.fireEvent('select', this, o, v);
22718 getValue: function()
22723 onClick: function(e)
22725 e.stopPropagation();
22726 e.preventDefault();
22728 var target = e.getTarget();
22730 if(target.nodeName.toLowerCase() === 'i'){
22731 target = Roo.get(target).dom.parentNode;
22734 var nodeName = target.nodeName;
22735 var className = target.className;
22736 var html = target.innerHTML;
22738 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22742 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22744 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22750 picker : function()
22752 return this.pickerEl;
22755 fillMonths: function()
22758 var months = this.picker().select('>.datepicker-months td', true).first();
22760 months.dom.innerHTML = '';
22766 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22769 months.createChild(month);
22778 if(typeof(this.vIndex) == 'undefined' && this.value.length){
22779 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22782 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22783 e.removeClass('active');
22785 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22786 e.addClass('active');
22793 if(this.isInline) {
22797 this.picker().removeClass(['bottom', 'top']);
22799 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22801 * place to the top of element!
22805 this.picker().addClass('top');
22806 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22811 this.picker().addClass('bottom');
22813 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22816 onFocus : function()
22818 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22822 onBlur : function()
22824 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22826 var d = this.inputEl().getValue();
22835 this.picker().show();
22836 this.picker().select('>.datepicker-months', true).first().show();
22840 this.fireEvent('show', this, this.date);
22845 if(this.isInline) {
22848 this.picker().hide();
22849 this.fireEvent('hide', this, this.date);
22853 onMousedown: function(e)
22855 e.stopPropagation();
22856 e.preventDefault();
22861 Roo.bootstrap.MonthField.superclass.keyup.call(this);
22865 fireKey: function(e)
22867 if (!this.picker().isVisible()){
22868 if (e.keyCode == 27) {// allow escape to hide and re-show picker
22879 e.preventDefault();
22883 dir = e.keyCode == 37 ? -1 : 1;
22885 this.vIndex = this.vIndex + dir;
22887 if(this.vIndex < 0){
22891 if(this.vIndex > 11){
22895 if(isNaN(this.vIndex)){
22899 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22905 dir = e.keyCode == 38 ? -1 : 1;
22907 this.vIndex = this.vIndex + dir * 4;
22909 if(this.vIndex < 0){
22913 if(this.vIndex > 11){
22917 if(isNaN(this.vIndex)){
22921 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22926 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22927 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22931 e.preventDefault();
22934 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22935 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22951 this.picker().remove();
22956 Roo.apply(Roo.bootstrap.MonthField, {
22975 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22976 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22981 Roo.apply(Roo.bootstrap.MonthField, {
22985 cls: 'datepicker dropdown-menu roo-dynamic',
22989 cls: 'datepicker-months',
22993 cls: 'table-condensed',
22995 Roo.bootstrap.DateField.content
23015 * @class Roo.bootstrap.CheckBox
23016 * @extends Roo.bootstrap.Input
23017 * Bootstrap CheckBox class
23019 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23020 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23021 * @cfg {String} boxLabel The text that appears beside the checkbox
23022 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23023 * @cfg {Boolean} checked initnal the element
23024 * @cfg {Boolean} inline inline the element (default false)
23025 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23026 * @cfg {String} tooltip label tooltip
23029 * Create a new CheckBox
23030 * @param {Object} config The config object
23033 Roo.bootstrap.CheckBox = function(config){
23034 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23039 * Fires when the element is checked or unchecked.
23040 * @param {Roo.bootstrap.CheckBox} this This input
23041 * @param {Boolean} checked The new checked value
23046 * Fires when the element is click.
23047 * @param {Roo.bootstrap.CheckBox} this This input
23054 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23056 inputType: 'checkbox',
23065 // checkbox success does not make any sense really..
23070 getAutoCreate : function()
23072 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23078 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23081 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23087 type : this.inputType,
23088 value : this.inputValue,
23089 cls : 'roo-' + this.inputType, //'form-box',
23090 placeholder : this.placeholder || ''
23094 if(this.inputType != 'radio'){
23098 cls : 'roo-hidden-value',
23099 value : this.checked ? this.inputValue : this.valueOff
23104 if (this.weight) { // Validity check?
23105 cfg.cls += " " + this.inputType + "-" + this.weight;
23108 if (this.disabled) {
23109 input.disabled=true;
23113 input.checked = this.checked;
23118 input.name = this.name;
23120 if(this.inputType != 'radio'){
23121 hidden.name = this.name;
23122 input.name = '_hidden_' + this.name;
23127 input.cls += ' input-' + this.size;
23132 ['xs','sm','md','lg'].map(function(size){
23133 if (settings[size]) {
23134 cfg.cls += ' col-' + size + '-' + settings[size];
23138 var inputblock = input;
23140 if (this.before || this.after) {
23143 cls : 'input-group',
23148 inputblock.cn.push({
23150 cls : 'input-group-addon',
23155 inputblock.cn.push(input);
23157 if(this.inputType != 'radio'){
23158 inputblock.cn.push(hidden);
23162 inputblock.cn.push({
23164 cls : 'input-group-addon',
23170 var boxLabelCfg = false;
23176 //'for': id, // box label is handled by onclick - so no for...
23178 html: this.boxLabel
23181 boxLabelCfg.tooltip = this.tooltip;
23187 if (align ==='left' && this.fieldLabel.length) {
23188 // Roo.log("left and has label");
23193 cls : 'control-label',
23194 html : this.fieldLabel
23205 cfg.cn[1].cn.push(boxLabelCfg);
23208 if(this.labelWidth > 12){
23209 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23212 if(this.labelWidth < 13 && this.labelmd == 0){
23213 this.labelmd = this.labelWidth;
23216 if(this.labellg > 0){
23217 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23218 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23221 if(this.labelmd > 0){
23222 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23223 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23226 if(this.labelsm > 0){
23227 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23228 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23231 if(this.labelxs > 0){
23232 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23233 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23236 } else if ( this.fieldLabel.length) {
23237 // Roo.log(" label");
23241 tag: this.boxLabel ? 'span' : 'label',
23243 cls: 'control-label box-input-label',
23244 //cls : 'input-group-addon',
23245 html : this.fieldLabel
23252 cfg.cn.push(boxLabelCfg);
23257 // Roo.log(" no label && no align");
23258 cfg.cn = [ inputblock ] ;
23260 cfg.cn.push(boxLabelCfg);
23268 if(this.inputType != 'radio'){
23269 cfg.cn.push(hidden);
23277 * return the real input element.
23279 inputEl: function ()
23281 return this.el.select('input.roo-' + this.inputType,true).first();
23283 hiddenEl: function ()
23285 return this.el.select('input.roo-hidden-value',true).first();
23288 labelEl: function()
23290 return this.el.select('label.control-label',true).first();
23292 /* depricated... */
23296 return this.labelEl();
23299 boxLabelEl: function()
23301 return this.el.select('label.box-label',true).first();
23304 initEvents : function()
23306 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23308 this.inputEl().on('click', this.onClick, this);
23310 if (this.boxLabel) {
23311 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23314 this.startValue = this.getValue();
23317 Roo.bootstrap.CheckBox.register(this);
23321 onClick : function(e)
23323 if(this.fireEvent('click', this, e) !== false){
23324 this.setChecked(!this.checked);
23329 setChecked : function(state,suppressEvent)
23331 this.startValue = this.getValue();
23333 if(this.inputType == 'radio'){
23335 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23336 e.dom.checked = false;
23339 this.inputEl().dom.checked = true;
23341 this.inputEl().dom.value = this.inputValue;
23343 if(suppressEvent !== true){
23344 this.fireEvent('check', this, true);
23352 this.checked = state;
23354 this.inputEl().dom.checked = state;
23357 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23359 if(suppressEvent !== true){
23360 this.fireEvent('check', this, state);
23366 getValue : function()
23368 if(this.inputType == 'radio'){
23369 return this.getGroupValue();
23372 return this.hiddenEl().dom.value;
23376 getGroupValue : function()
23378 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23382 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23385 setValue : function(v,suppressEvent)
23387 if(this.inputType == 'radio'){
23388 this.setGroupValue(v, suppressEvent);
23392 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23397 setGroupValue : function(v, suppressEvent)
23399 this.startValue = this.getValue();
23401 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23402 e.dom.checked = false;
23404 if(e.dom.value == v){
23405 e.dom.checked = true;
23409 if(suppressEvent !== true){
23410 this.fireEvent('check', this, true);
23418 validate : function()
23420 if(this.getVisibilityEl().hasClass('hidden')){
23426 (this.inputType == 'radio' && this.validateRadio()) ||
23427 (this.inputType == 'checkbox' && this.validateCheckbox())
23433 this.markInvalid();
23437 validateRadio : function()
23439 if(this.getVisibilityEl().hasClass('hidden')){
23443 if(this.allowBlank){
23449 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23450 if(!e.dom.checked){
23462 validateCheckbox : function()
23465 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23466 //return (this.getValue() == this.inputValue) ? true : false;
23469 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23477 for(var i in group){
23478 if(group[i].el.isVisible(true)){
23486 for(var i in group){
23491 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23498 * Mark this field as valid
23500 markValid : function()
23504 this.fireEvent('valid', this);
23506 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23509 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23516 if(this.inputType == 'radio'){
23517 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23518 var fg = e.findParent('.form-group', false, true);
23519 if (Roo.bootstrap.version == 3) {
23520 fg.removeClass([_this.invalidClass, _this.validClass]);
23521 fg.addClass(_this.validClass);
23523 fg.removeClass(['is-valid', 'is-invalid']);
23524 fg.addClass('is-valid');
23532 var fg = this.el.findParent('.form-group', false, true);
23533 if (Roo.bootstrap.version == 3) {
23534 fg.removeClass([this.invalidClass, this.validClass]);
23535 fg.addClass(this.validClass);
23537 fg.removeClass(['is-valid', 'is-invalid']);
23538 fg.addClass('is-valid');
23543 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23549 for(var i in group){
23550 var fg = group[i].el.findParent('.form-group', false, true);
23551 if (Roo.bootstrap.version == 3) {
23552 fg.removeClass([this.invalidClass, this.validClass]);
23553 fg.addClass(this.validClass);
23555 fg.removeClass(['is-valid', 'is-invalid']);
23556 fg.addClass('is-valid');
23562 * Mark this field as invalid
23563 * @param {String} msg The validation message
23565 markInvalid : function(msg)
23567 if(this.allowBlank){
23573 this.fireEvent('invalid', this, msg);
23575 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23578 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23582 label.markInvalid();
23585 if(this.inputType == 'radio'){
23587 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23588 var fg = e.findParent('.form-group', false, true);
23589 if (Roo.bootstrap.version == 3) {
23590 fg.removeClass([_this.invalidClass, _this.validClass]);
23591 fg.addClass(_this.invalidClass);
23593 fg.removeClass(['is-invalid', 'is-valid']);
23594 fg.addClass('is-invalid');
23602 var fg = this.el.findParent('.form-group', false, true);
23603 if (Roo.bootstrap.version == 3) {
23604 fg.removeClass([_this.invalidClass, _this.validClass]);
23605 fg.addClass(_this.invalidClass);
23607 fg.removeClass(['is-invalid', 'is-valid']);
23608 fg.addClass('is-invalid');
23613 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23619 for(var i in group){
23620 var fg = group[i].el.findParent('.form-group', false, true);
23621 if (Roo.bootstrap.version == 3) {
23622 fg.removeClass([_this.invalidClass, _this.validClass]);
23623 fg.addClass(_this.invalidClass);
23625 fg.removeClass(['is-invalid', 'is-valid']);
23626 fg.addClass('is-invalid');
23632 clearInvalid : function()
23634 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23636 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23638 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23640 if (label && label.iconEl) {
23641 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23642 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23646 disable : function()
23648 if(this.inputType != 'radio'){
23649 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23656 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23657 _this.getActionEl().addClass(this.disabledClass);
23658 e.dom.disabled = true;
23662 this.disabled = true;
23663 this.fireEvent("disable", this);
23667 enable : function()
23669 if(this.inputType != 'radio'){
23670 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23677 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23678 _this.getActionEl().removeClass(this.disabledClass);
23679 e.dom.disabled = false;
23683 this.disabled = false;
23684 this.fireEvent("enable", this);
23688 setBoxLabel : function(v)
23693 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23699 Roo.apply(Roo.bootstrap.CheckBox, {
23704 * register a CheckBox Group
23705 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23707 register : function(checkbox)
23709 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23710 this.groups[checkbox.groupId] = {};
23713 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23717 this.groups[checkbox.groupId][checkbox.name] = checkbox;
23721 * fetch a CheckBox Group based on the group ID
23722 * @param {string} the group ID
23723 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23725 get: function(groupId) {
23726 if (typeof(this.groups[groupId]) == 'undefined') {
23730 return this.groups[groupId] ;
23743 * @class Roo.bootstrap.Radio
23744 * @extends Roo.bootstrap.Component
23745 * Bootstrap Radio class
23746 * @cfg {String} boxLabel - the label associated
23747 * @cfg {String} value - the value of radio
23750 * Create a new Radio
23751 * @param {Object} config The config object
23753 Roo.bootstrap.Radio = function(config){
23754 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23758 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23764 getAutoCreate : function()
23768 cls : 'form-group radio',
23773 html : this.boxLabel
23781 initEvents : function()
23783 this.parent().register(this);
23785 this.el.on('click', this.onClick, this);
23789 onClick : function(e)
23791 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23792 this.setChecked(true);
23796 setChecked : function(state, suppressEvent)
23798 this.parent().setValue(this.value, suppressEvent);
23802 setBoxLabel : function(v)
23807 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23822 * @class Roo.bootstrap.SecurePass
23823 * @extends Roo.bootstrap.Input
23824 * Bootstrap SecurePass class
23828 * Create a new SecurePass
23829 * @param {Object} config The config object
23832 Roo.bootstrap.SecurePass = function (config) {
23833 // these go here, so the translation tool can replace them..
23835 PwdEmpty: "Please type a password, and then retype it to confirm.",
23836 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23837 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23838 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23839 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23840 FNInPwd: "Your password can't contain your first name. Please type a different password.",
23841 LNInPwd: "Your password can't contain your last name. Please type a different password.",
23842 TooWeak: "Your password is Too Weak."
23844 this.meterLabel = "Password strength:";
23845 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23846 this.meterClass = [
23847 "roo-password-meter-tooweak",
23848 "roo-password-meter-weak",
23849 "roo-password-meter-medium",
23850 "roo-password-meter-strong",
23851 "roo-password-meter-grey"
23856 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23859 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23861 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23863 * PwdEmpty: "Please type a password, and then retype it to confirm.",
23864 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23865 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23866 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23867 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23868 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
23869 * LNInPwd: "Your password can't contain your last name. Please type a different password."
23879 * @cfg {String/Object} Label for the strength meter (defaults to
23880 * 'Password strength:')
23885 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23886 * ['Weak', 'Medium', 'Strong'])
23889 pwdStrengths: false,
23902 initEvents: function ()
23904 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23906 if (this.el.is('input[type=password]') && Roo.isSafari) {
23907 this.el.on('keydown', this.SafariOnKeyDown, this);
23910 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23913 onRender: function (ct, position)
23915 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23916 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23917 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23919 this.trigger.createChild({
23924 cls: 'roo-password-meter-grey col-xs-12',
23927 //width: this.meterWidth + 'px'
23931 cls: 'roo-password-meter-text'
23937 if (this.hideTrigger) {
23938 this.trigger.setDisplayed(false);
23940 this.setSize(this.width || '', this.height || '');
23943 onDestroy: function ()
23945 if (this.trigger) {
23946 this.trigger.removeAllListeners();
23947 this.trigger.remove();
23950 this.wrap.remove();
23952 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23955 checkStrength: function ()
23957 var pwd = this.inputEl().getValue();
23958 if (pwd == this._lastPwd) {
23963 if (this.ClientSideStrongPassword(pwd)) {
23965 } else if (this.ClientSideMediumPassword(pwd)) {
23967 } else if (this.ClientSideWeakPassword(pwd)) {
23973 Roo.log('strength1: ' + strength);
23975 //var pm = this.trigger.child('div/div/div').dom;
23976 var pm = this.trigger.child('div/div');
23977 pm.removeClass(this.meterClass);
23978 pm.addClass(this.meterClass[strength]);
23981 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
23983 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
23985 this._lastPwd = pwd;
23989 Roo.bootstrap.SecurePass.superclass.reset.call(this);
23991 this._lastPwd = '';
23993 var pm = this.trigger.child('div/div');
23994 pm.removeClass(this.meterClass);
23995 pm.addClass('roo-password-meter-grey');
23998 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24001 this.inputEl().dom.type='password';
24004 validateValue: function (value)
24006 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24009 if (value.length == 0) {
24010 if (this.allowBlank) {
24011 this.clearInvalid();
24015 this.markInvalid(this.errors.PwdEmpty);
24016 this.errorMsg = this.errors.PwdEmpty;
24024 if (!value.match(/[\x21-\x7e]+/)) {
24025 this.markInvalid(this.errors.PwdBadChar);
24026 this.errorMsg = this.errors.PwdBadChar;
24029 if (value.length < 6) {
24030 this.markInvalid(this.errors.PwdShort);
24031 this.errorMsg = this.errors.PwdShort;
24034 if (value.length > 16) {
24035 this.markInvalid(this.errors.PwdLong);
24036 this.errorMsg = this.errors.PwdLong;
24040 if (this.ClientSideStrongPassword(value)) {
24042 } else if (this.ClientSideMediumPassword(value)) {
24044 } else if (this.ClientSideWeakPassword(value)) {
24051 if (strength < 2) {
24052 //this.markInvalid(this.errors.TooWeak);
24053 this.errorMsg = this.errors.TooWeak;
24058 console.log('strength2: ' + strength);
24060 //var pm = this.trigger.child('div/div/div').dom;
24062 var pm = this.trigger.child('div/div');
24063 pm.removeClass(this.meterClass);
24064 pm.addClass(this.meterClass[strength]);
24066 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24068 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24070 this.errorMsg = '';
24074 CharacterSetChecks: function (type)
24077 this.fResult = false;
24080 isctype: function (character, type)
24083 case this.kCapitalLetter:
24084 if (character >= 'A' && character <= 'Z') {
24089 case this.kSmallLetter:
24090 if (character >= 'a' && character <= 'z') {
24096 if (character >= '0' && character <= '9') {
24101 case this.kPunctuation:
24102 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24113 IsLongEnough: function (pwd, size)
24115 return !(pwd == null || isNaN(size) || pwd.length < size);
24118 SpansEnoughCharacterSets: function (word, nb)
24120 if (!this.IsLongEnough(word, nb))
24125 var characterSetChecks = new Array(
24126 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24127 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24130 for (var index = 0; index < word.length; ++index) {
24131 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24132 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24133 characterSetChecks[nCharSet].fResult = true;
24140 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24141 if (characterSetChecks[nCharSet].fResult) {
24146 if (nCharSets < nb) {
24152 ClientSideStrongPassword: function (pwd)
24154 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24157 ClientSideMediumPassword: function (pwd)
24159 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24162 ClientSideWeakPassword: function (pwd)
24164 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24167 })//<script type="text/javascript">
24170 * Based Ext JS Library 1.1.1
24171 * Copyright(c) 2006-2007, Ext JS, LLC.
24177 * @class Roo.HtmlEditorCore
24178 * @extends Roo.Component
24179 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24181 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24184 Roo.HtmlEditorCore = function(config){
24187 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24192 * @event initialize
24193 * Fires when the editor is fully initialized (including the iframe)
24194 * @param {Roo.HtmlEditorCore} this
24199 * Fires when the editor is first receives the focus. Any insertion must wait
24200 * until after this event.
24201 * @param {Roo.HtmlEditorCore} this
24205 * @event beforesync
24206 * Fires before the textarea is updated with content from the editor iframe. Return false
24207 * to cancel the sync.
24208 * @param {Roo.HtmlEditorCore} this
24209 * @param {String} html
24213 * @event beforepush
24214 * Fires before the iframe editor is updated with content from the textarea. Return false
24215 * to cancel the push.
24216 * @param {Roo.HtmlEditorCore} this
24217 * @param {String} html
24222 * Fires when the textarea is updated with content from the editor iframe.
24223 * @param {Roo.HtmlEditorCore} this
24224 * @param {String} html
24229 * Fires when the iframe editor is updated with content from the textarea.
24230 * @param {Roo.HtmlEditorCore} this
24231 * @param {String} html
24236 * @event editorevent
24237 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24238 * @param {Roo.HtmlEditorCore} this
24244 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24246 // defaults : white / black...
24247 this.applyBlacklists();
24254 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24258 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24264 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24269 * @cfg {Number} height (in pixels)
24273 * @cfg {Number} width (in pixels)
24278 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24281 stylesheets: false,
24286 // private properties
24287 validationEvent : false,
24289 initialized : false,
24291 sourceEditMode : false,
24292 onFocus : Roo.emptyFn,
24294 hideMode:'offsets',
24298 // blacklist + whitelisted elements..
24305 * Protected method that will not generally be called directly. It
24306 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24307 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24309 getDocMarkup : function(){
24313 // inherit styels from page...??
24314 if (this.stylesheets === false) {
24316 Roo.get(document.head).select('style').each(function(node) {
24317 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24320 Roo.get(document.head).select('link').each(function(node) {
24321 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24324 } else if (!this.stylesheets.length) {
24326 st = '<style type="text/css">' +
24327 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24330 for (var i in this.stylesheets) {
24331 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24336 st += '<style type="text/css">' +
24337 'IMG { cursor: pointer } ' +
24340 var cls = 'roo-htmleditor-body';
24342 if(this.bodyCls.length){
24343 cls += ' ' + this.bodyCls;
24346 return '<html><head>' + st +
24347 //<style type="text/css">' +
24348 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24350 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24354 onRender : function(ct, position)
24357 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24358 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24361 this.el.dom.style.border = '0 none';
24362 this.el.dom.setAttribute('tabIndex', -1);
24363 this.el.addClass('x-hidden hide');
24367 if(Roo.isIE){ // fix IE 1px bogus margin
24368 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24372 this.frameId = Roo.id();
24376 var iframe = this.owner.wrap.createChild({
24378 cls: 'form-control', // bootstrap..
24380 name: this.frameId,
24381 frameBorder : 'no',
24382 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24387 this.iframe = iframe.dom;
24389 this.assignDocWin();
24391 this.doc.designMode = 'on';
24394 this.doc.write(this.getDocMarkup());
24398 var task = { // must defer to wait for browser to be ready
24400 //console.log("run task?" + this.doc.readyState);
24401 this.assignDocWin();
24402 if(this.doc.body || this.doc.readyState == 'complete'){
24404 this.doc.designMode="on";
24408 Roo.TaskMgr.stop(task);
24409 this.initEditor.defer(10, this);
24416 Roo.TaskMgr.start(task);
24421 onResize : function(w, h)
24423 Roo.log('resize: ' +w + ',' + h );
24424 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24428 if(typeof w == 'number'){
24430 this.iframe.style.width = w + 'px';
24432 if(typeof h == 'number'){
24434 this.iframe.style.height = h + 'px';
24436 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24443 * Toggles the editor between standard and source edit mode.
24444 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24446 toggleSourceEdit : function(sourceEditMode){
24448 this.sourceEditMode = sourceEditMode === true;
24450 if(this.sourceEditMode){
24452 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24455 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24456 //this.iframe.className = '';
24459 //this.setSize(this.owner.wrap.getSize());
24460 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24467 * Protected method that will not generally be called directly. If you need/want
24468 * custom HTML cleanup, this is the method you should override.
24469 * @param {String} html The HTML to be cleaned
24470 * return {String} The cleaned HTML
24472 cleanHtml : function(html){
24473 html = String(html);
24474 if(html.length > 5){
24475 if(Roo.isSafari){ // strip safari nonsense
24476 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24479 if(html == ' '){
24486 * HTML Editor -> Textarea
24487 * Protected method that will not generally be called directly. Syncs the contents
24488 * of the editor iframe with the textarea.
24490 syncValue : function(){
24491 if(this.initialized){
24492 var bd = (this.doc.body || this.doc.documentElement);
24493 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24494 var html = bd.innerHTML;
24496 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24497 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24499 html = '<div style="'+m[0]+'">' + html + '</div>';
24502 html = this.cleanHtml(html);
24503 // fix up the special chars.. normaly like back quotes in word...
24504 // however we do not want to do this with chinese..
24505 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24507 var cc = match.charCodeAt();
24509 // Get the character value, handling surrogate pairs
24510 if (match.length == 2) {
24511 // It's a surrogate pair, calculate the Unicode code point
24512 var high = match.charCodeAt(0) - 0xD800;
24513 var low = match.charCodeAt(1) - 0xDC00;
24514 cc = (high * 0x400) + low + 0x10000;
24516 (cc >= 0x4E00 && cc < 0xA000 ) ||
24517 (cc >= 0x3400 && cc < 0x4E00 ) ||
24518 (cc >= 0xf900 && cc < 0xfb00 )
24523 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24524 return "&#" + cc + ";";
24531 if(this.owner.fireEvent('beforesync', this, html) !== false){
24532 this.el.dom.value = html;
24533 this.owner.fireEvent('sync', this, html);
24539 * Protected method that will not generally be called directly. Pushes the value of the textarea
24540 * into the iframe editor.
24542 pushValue : function(){
24543 if(this.initialized){
24544 var v = this.el.dom.value.trim();
24546 // if(v.length < 1){
24550 if(this.owner.fireEvent('beforepush', this, v) !== false){
24551 var d = (this.doc.body || this.doc.documentElement);
24553 this.cleanUpPaste();
24554 this.el.dom.value = d.innerHTML;
24555 this.owner.fireEvent('push', this, v);
24561 deferFocus : function(){
24562 this.focus.defer(10, this);
24566 focus : function(){
24567 if(this.win && !this.sourceEditMode){
24574 assignDocWin: function()
24576 var iframe = this.iframe;
24579 this.doc = iframe.contentWindow.document;
24580 this.win = iframe.contentWindow;
24582 // if (!Roo.get(this.frameId)) {
24585 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24586 // this.win = Roo.get(this.frameId).dom.contentWindow;
24588 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24592 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24593 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24598 initEditor : function(){
24599 //console.log("INIT EDITOR");
24600 this.assignDocWin();
24604 this.doc.designMode="on";
24606 this.doc.write(this.getDocMarkup());
24609 var dbody = (this.doc.body || this.doc.documentElement);
24610 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24611 // this copies styles from the containing element into thsi one..
24612 // not sure why we need all of this..
24613 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24615 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24616 //ss['background-attachment'] = 'fixed'; // w3c
24617 dbody.bgProperties = 'fixed'; // ie
24618 //Roo.DomHelper.applyStyles(dbody, ss);
24619 Roo.EventManager.on(this.doc, {
24620 //'mousedown': this.onEditorEvent,
24621 'mouseup': this.onEditorEvent,
24622 'dblclick': this.onEditorEvent,
24623 'click': this.onEditorEvent,
24624 'keyup': this.onEditorEvent,
24629 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24631 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24632 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24634 this.initialized = true;
24636 this.owner.fireEvent('initialize', this);
24641 onDestroy : function(){
24647 //for (var i =0; i < this.toolbars.length;i++) {
24648 // // fixme - ask toolbars for heights?
24649 // this.toolbars[i].onDestroy();
24652 //this.wrap.dom.innerHTML = '';
24653 //this.wrap.remove();
24658 onFirstFocus : function(){
24660 this.assignDocWin();
24663 this.activated = true;
24666 if(Roo.isGecko){ // prevent silly gecko errors
24668 var s = this.win.getSelection();
24669 if(!s.focusNode || s.focusNode.nodeType != 3){
24670 var r = s.getRangeAt(0);
24671 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24676 this.execCmd('useCSS', true);
24677 this.execCmd('styleWithCSS', false);
24680 this.owner.fireEvent('activate', this);
24684 adjustFont: function(btn){
24685 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24686 //if(Roo.isSafari){ // safari
24689 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24690 if(Roo.isSafari){ // safari
24691 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24692 v = (v < 10) ? 10 : v;
24693 v = (v > 48) ? 48 : v;
24694 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24699 v = Math.max(1, v+adjust);
24701 this.execCmd('FontSize', v );
24704 onEditorEvent : function(e)
24706 this.owner.fireEvent('editorevent', this, e);
24707 // this.updateToolbar();
24708 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24711 insertTag : function(tg)
24713 // could be a bit smarter... -> wrap the current selected tRoo..
24714 if (tg.toLowerCase() == 'span' ||
24715 tg.toLowerCase() == 'code' ||
24716 tg.toLowerCase() == 'sup' ||
24717 tg.toLowerCase() == 'sub'
24720 range = this.createRange(this.getSelection());
24721 var wrappingNode = this.doc.createElement(tg.toLowerCase());
24722 wrappingNode.appendChild(range.extractContents());
24723 range.insertNode(wrappingNode);
24730 this.execCmd("formatblock", tg);
24734 insertText : function(txt)
24738 var range = this.createRange();
24739 range.deleteContents();
24740 //alert(Sender.getAttribute('label'));
24742 range.insertNode(this.doc.createTextNode(txt));
24748 * Executes a Midas editor command on the editor document and performs necessary focus and
24749 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24750 * @param {String} cmd The Midas command
24751 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24753 relayCmd : function(cmd, value){
24755 this.execCmd(cmd, value);
24756 this.owner.fireEvent('editorevent', this);
24757 //this.updateToolbar();
24758 this.owner.deferFocus();
24762 * Executes a Midas editor command directly on the editor document.
24763 * For visual commands, you should use {@link #relayCmd} instead.
24764 * <b>This should only be called after the editor is initialized.</b>
24765 * @param {String} cmd The Midas command
24766 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24768 execCmd : function(cmd, value){
24769 this.doc.execCommand(cmd, false, value === undefined ? null : value);
24776 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24778 * @param {String} text | dom node..
24780 insertAtCursor : function(text)
24783 if(!this.activated){
24789 var r = this.doc.selection.createRange();
24800 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24804 // from jquery ui (MIT licenced)
24806 var win = this.win;
24808 if (win.getSelection && win.getSelection().getRangeAt) {
24809 range = win.getSelection().getRangeAt(0);
24810 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24811 range.insertNode(node);
24812 } else if (win.document.selection && win.document.selection.createRange) {
24813 // no firefox support
24814 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24815 win.document.selection.createRange().pasteHTML(txt);
24817 // no firefox support
24818 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24819 this.execCmd('InsertHTML', txt);
24828 mozKeyPress : function(e){
24830 var c = e.getCharCode(), cmd;
24833 c = String.fromCharCode(c).toLowerCase();
24847 this.cleanUpPaste.defer(100, this);
24855 e.preventDefault();
24863 fixKeys : function(){ // load time branching for fastest keydown performance
24865 return function(e){
24866 var k = e.getKey(), r;
24869 r = this.doc.selection.createRange();
24872 r.pasteHTML('    ');
24879 r = this.doc.selection.createRange();
24881 var target = r.parentElement();
24882 if(!target || target.tagName.toLowerCase() != 'li'){
24884 r.pasteHTML('<br />');
24890 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24891 this.cleanUpPaste.defer(100, this);
24897 }else if(Roo.isOpera){
24898 return function(e){
24899 var k = e.getKey();
24903 this.execCmd('InsertHTML','    ');
24906 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24907 this.cleanUpPaste.defer(100, this);
24912 }else if(Roo.isSafari){
24913 return function(e){
24914 var k = e.getKey();
24918 this.execCmd('InsertText','\t');
24922 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24923 this.cleanUpPaste.defer(100, this);
24931 getAllAncestors: function()
24933 var p = this.getSelectedNode();
24936 a.push(p); // push blank onto stack..
24937 p = this.getParentElement();
24941 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24945 a.push(this.doc.body);
24949 lastSelNode : false,
24952 getSelection : function()
24954 this.assignDocWin();
24955 return Roo.isIE ? this.doc.selection : this.win.getSelection();
24958 getSelectedNode: function()
24960 // this may only work on Gecko!!!
24962 // should we cache this!!!!
24967 var range = this.createRange(this.getSelection()).cloneRange();
24970 var parent = range.parentElement();
24972 var testRange = range.duplicate();
24973 testRange.moveToElementText(parent);
24974 if (testRange.inRange(range)) {
24977 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24980 parent = parent.parentElement;
24985 // is ancestor a text element.
24986 var ac = range.commonAncestorContainer;
24987 if (ac.nodeType == 3) {
24988 ac = ac.parentNode;
24991 var ar = ac.childNodes;
24994 var other_nodes = [];
24995 var has_other_nodes = false;
24996 for (var i=0;i<ar.length;i++) {
24997 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25000 // fullly contained node.
25002 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25007 // probably selected..
25008 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25009 other_nodes.push(ar[i]);
25013 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25018 has_other_nodes = true;
25020 if (!nodes.length && other_nodes.length) {
25021 nodes= other_nodes;
25023 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25029 createRange: function(sel)
25031 // this has strange effects when using with
25032 // top toolbar - not sure if it's a great idea.
25033 //this.editor.contentWindow.focus();
25034 if (typeof sel != "undefined") {
25036 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25038 return this.doc.createRange();
25041 return this.doc.createRange();
25044 getParentElement: function()
25047 this.assignDocWin();
25048 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25050 var range = this.createRange(sel);
25053 var p = range.commonAncestorContainer;
25054 while (p.nodeType == 3) { // text node
25065 * Range intersection.. the hard stuff...
25069 * [ -- selected range --- ]
25073 * if end is before start or hits it. fail.
25074 * if start is after end or hits it fail.
25076 * if either hits (but other is outside. - then it's not
25082 // @see http://www.thismuchiknow.co.uk/?p=64.
25083 rangeIntersectsNode : function(range, node)
25085 var nodeRange = node.ownerDocument.createRange();
25087 nodeRange.selectNode(node);
25089 nodeRange.selectNodeContents(node);
25092 var rangeStartRange = range.cloneRange();
25093 rangeStartRange.collapse(true);
25095 var rangeEndRange = range.cloneRange();
25096 rangeEndRange.collapse(false);
25098 var nodeStartRange = nodeRange.cloneRange();
25099 nodeStartRange.collapse(true);
25101 var nodeEndRange = nodeRange.cloneRange();
25102 nodeEndRange.collapse(false);
25104 return rangeStartRange.compareBoundaryPoints(
25105 Range.START_TO_START, nodeEndRange) == -1 &&
25106 rangeEndRange.compareBoundaryPoints(
25107 Range.START_TO_START, nodeStartRange) == 1;
25111 rangeCompareNode : function(range, node)
25113 var nodeRange = node.ownerDocument.createRange();
25115 nodeRange.selectNode(node);
25117 nodeRange.selectNodeContents(node);
25121 range.collapse(true);
25123 nodeRange.collapse(true);
25125 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25126 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25128 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25130 var nodeIsBefore = ss == 1;
25131 var nodeIsAfter = ee == -1;
25133 if (nodeIsBefore && nodeIsAfter) {
25136 if (!nodeIsBefore && nodeIsAfter) {
25137 return 1; //right trailed.
25140 if (nodeIsBefore && !nodeIsAfter) {
25141 return 2; // left trailed.
25147 // private? - in a new class?
25148 cleanUpPaste : function()
25150 // cleans up the whole document..
25151 Roo.log('cleanuppaste');
25153 this.cleanUpChildren(this.doc.body);
25154 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25155 if (clean != this.doc.body.innerHTML) {
25156 this.doc.body.innerHTML = clean;
25161 cleanWordChars : function(input) {// change the chars to hex code
25162 var he = Roo.HtmlEditorCore;
25164 var output = input;
25165 Roo.each(he.swapCodes, function(sw) {
25166 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25168 output = output.replace(swapper, sw[1]);
25175 cleanUpChildren : function (n)
25177 if (!n.childNodes.length) {
25180 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25181 this.cleanUpChild(n.childNodes[i]);
25188 cleanUpChild : function (node)
25191 //console.log(node);
25192 if (node.nodeName == "#text") {
25193 // clean up silly Windows -- stuff?
25196 if (node.nodeName == "#comment") {
25197 node.parentNode.removeChild(node);
25198 // clean up silly Windows -- stuff?
25201 var lcname = node.tagName.toLowerCase();
25202 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25203 // whitelist of tags..
25205 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25207 node.parentNode.removeChild(node);
25212 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25214 // spans with no attributes - just remove them..
25215 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25216 remove_keep_children = true;
25219 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25220 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25222 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25223 // remove_keep_children = true;
25226 if (remove_keep_children) {
25227 this.cleanUpChildren(node);
25228 // inserts everything just before this node...
25229 while (node.childNodes.length) {
25230 var cn = node.childNodes[0];
25231 node.removeChild(cn);
25232 node.parentNode.insertBefore(cn, node);
25234 node.parentNode.removeChild(node);
25238 if (!node.attributes || !node.attributes.length) {
25243 this.cleanUpChildren(node);
25247 function cleanAttr(n,v)
25250 if (v.match(/^\./) || v.match(/^\//)) {
25253 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25256 if (v.match(/^#/)) {
25259 if (v.match(/^\{/)) { // allow template editing.
25262 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25263 node.removeAttribute(n);
25267 var cwhite = this.cwhite;
25268 var cblack = this.cblack;
25270 function cleanStyle(n,v)
25272 if (v.match(/expression/)) { //XSS?? should we even bother..
25273 node.removeAttribute(n);
25277 var parts = v.split(/;/);
25280 Roo.each(parts, function(p) {
25281 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25285 var l = p.split(':').shift().replace(/\s+/g,'');
25286 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25288 if ( cwhite.length && cblack.indexOf(l) > -1) {
25289 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25290 //node.removeAttribute(n);
25294 // only allow 'c whitelisted system attributes'
25295 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25296 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25297 //node.removeAttribute(n);
25307 if (clean.length) {
25308 node.setAttribute(n, clean.join(';'));
25310 node.removeAttribute(n);
25316 for (var i = node.attributes.length-1; i > -1 ; i--) {
25317 var a = node.attributes[i];
25320 if (a.name.toLowerCase().substr(0,2)=='on') {
25321 node.removeAttribute(a.name);
25324 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25325 node.removeAttribute(a.name);
25328 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25329 cleanAttr(a.name,a.value); // fixme..
25332 if (a.name == 'style') {
25333 cleanStyle(a.name,a.value);
25336 /// clean up MS crap..
25337 // tecnically this should be a list of valid class'es..
25340 if (a.name == 'class') {
25341 if (a.value.match(/^Mso/)) {
25342 node.removeAttribute('class');
25345 if (a.value.match(/^body$/)) {
25346 node.removeAttribute('class');
25357 this.cleanUpChildren(node);
25363 * Clean up MS wordisms...
25365 cleanWord : function(node)
25368 this.cleanWord(this.doc.body);
25373 node.nodeName == 'SPAN' &&
25374 !node.hasAttributes() &&
25375 node.childNodes.length == 1 &&
25376 node.firstChild.nodeName == "#text"
25378 var textNode = node.firstChild;
25379 node.removeChild(textNode);
25380 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25381 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25383 node.parentNode.insertBefore(textNode, node);
25384 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25385 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25387 node.parentNode.removeChild(node);
25390 if (node.nodeName == "#text") {
25391 // clean up silly Windows -- stuff?
25394 if (node.nodeName == "#comment") {
25395 node.parentNode.removeChild(node);
25396 // clean up silly Windows -- stuff?
25400 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25401 node.parentNode.removeChild(node);
25404 //Roo.log(node.tagName);
25405 // remove - but keep children..
25406 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25407 //Roo.log('-- removed');
25408 while (node.childNodes.length) {
25409 var cn = node.childNodes[0];
25410 node.removeChild(cn);
25411 node.parentNode.insertBefore(cn, node);
25412 // move node to parent - and clean it..
25413 this.cleanWord(cn);
25415 node.parentNode.removeChild(node);
25416 /// no need to iterate chidlren = it's got none..
25417 //this.iterateChildren(node, this.cleanWord);
25421 if (node.className.length) {
25423 var cn = node.className.split(/\W+/);
25425 Roo.each(cn, function(cls) {
25426 if (cls.match(/Mso[a-zA-Z]+/)) {
25431 node.className = cna.length ? cna.join(' ') : '';
25433 node.removeAttribute("class");
25437 if (node.hasAttribute("lang")) {
25438 node.removeAttribute("lang");
25441 if (node.hasAttribute("style")) {
25443 var styles = node.getAttribute("style").split(";");
25445 Roo.each(styles, function(s) {
25446 if (!s.match(/:/)) {
25449 var kv = s.split(":");
25450 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25453 // what ever is left... we allow.
25456 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25457 if (!nstyle.length) {
25458 node.removeAttribute('style');
25461 this.iterateChildren(node, this.cleanWord);
25467 * iterateChildren of a Node, calling fn each time, using this as the scole..
25468 * @param {DomNode} node node to iterate children of.
25469 * @param {Function} fn method of this class to call on each item.
25471 iterateChildren : function(node, fn)
25473 if (!node.childNodes.length) {
25476 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25477 fn.call(this, node.childNodes[i])
25483 * cleanTableWidths.
25485 * Quite often pasting from word etc.. results in tables with column and widths.
25486 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25489 cleanTableWidths : function(node)
25494 this.cleanTableWidths(this.doc.body);
25499 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25502 Roo.log(node.tagName);
25503 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25504 this.iterateChildren(node, this.cleanTableWidths);
25507 if (node.hasAttribute('width')) {
25508 node.removeAttribute('width');
25512 if (node.hasAttribute("style")) {
25515 var styles = node.getAttribute("style").split(";");
25517 Roo.each(styles, function(s) {
25518 if (!s.match(/:/)) {
25521 var kv = s.split(":");
25522 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25525 // what ever is left... we allow.
25528 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25529 if (!nstyle.length) {
25530 node.removeAttribute('style');
25534 this.iterateChildren(node, this.cleanTableWidths);
25542 domToHTML : function(currentElement, depth, nopadtext) {
25544 depth = depth || 0;
25545 nopadtext = nopadtext || false;
25547 if (!currentElement) {
25548 return this.domToHTML(this.doc.body);
25551 //Roo.log(currentElement);
25553 var allText = false;
25554 var nodeName = currentElement.nodeName;
25555 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25557 if (nodeName == '#text') {
25559 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25564 if (nodeName != 'BODY') {
25567 // Prints the node tagName, such as <A>, <IMG>, etc
25570 for(i = 0; i < currentElement.attributes.length;i++) {
25572 var aname = currentElement.attributes.item(i).name;
25573 if (!currentElement.attributes.item(i).value.length) {
25576 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25579 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25588 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25591 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25596 // Traverse the tree
25598 var currentElementChild = currentElement.childNodes.item(i);
25599 var allText = true;
25600 var innerHTML = '';
25602 while (currentElementChild) {
25603 // Formatting code (indent the tree so it looks nice on the screen)
25604 var nopad = nopadtext;
25605 if (lastnode == 'SPAN') {
25609 if (currentElementChild.nodeName == '#text') {
25610 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25611 toadd = nopadtext ? toadd : toadd.trim();
25612 if (!nopad && toadd.length > 80) {
25613 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25615 innerHTML += toadd;
25618 currentElementChild = currentElement.childNodes.item(i);
25624 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25626 // Recursively traverse the tree structure of the child node
25627 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25628 lastnode = currentElementChild.nodeName;
25630 currentElementChild=currentElement.childNodes.item(i);
25636 // The remaining code is mostly for formatting the tree
25637 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25642 ret+= "</"+tagName+">";
25648 applyBlacklists : function()
25650 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25651 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25655 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25656 if (b.indexOf(tag) > -1) {
25659 this.white.push(tag);
25663 Roo.each(w, function(tag) {
25664 if (b.indexOf(tag) > -1) {
25667 if (this.white.indexOf(tag) > -1) {
25670 this.white.push(tag);
25675 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25676 if (w.indexOf(tag) > -1) {
25679 this.black.push(tag);
25683 Roo.each(b, function(tag) {
25684 if (w.indexOf(tag) > -1) {
25687 if (this.black.indexOf(tag) > -1) {
25690 this.black.push(tag);
25695 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25696 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
25700 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25701 if (b.indexOf(tag) > -1) {
25704 this.cwhite.push(tag);
25708 Roo.each(w, function(tag) {
25709 if (b.indexOf(tag) > -1) {
25712 if (this.cwhite.indexOf(tag) > -1) {
25715 this.cwhite.push(tag);
25720 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25721 if (w.indexOf(tag) > -1) {
25724 this.cblack.push(tag);
25728 Roo.each(b, function(tag) {
25729 if (w.indexOf(tag) > -1) {
25732 if (this.cblack.indexOf(tag) > -1) {
25735 this.cblack.push(tag);
25740 setStylesheets : function(stylesheets)
25742 if(typeof(stylesheets) == 'string'){
25743 Roo.get(this.iframe.contentDocument.head).createChild({
25745 rel : 'stylesheet',
25754 Roo.each(stylesheets, function(s) {
25759 Roo.get(_this.iframe.contentDocument.head).createChild({
25761 rel : 'stylesheet',
25770 removeStylesheets : function()
25774 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25779 setStyle : function(style)
25781 Roo.get(this.iframe.contentDocument.head).createChild({
25790 // hide stuff that is not compatible
25804 * @event specialkey
25808 * @cfg {String} fieldClass @hide
25811 * @cfg {String} focusClass @hide
25814 * @cfg {String} autoCreate @hide
25817 * @cfg {String} inputType @hide
25820 * @cfg {String} invalidClass @hide
25823 * @cfg {String} invalidText @hide
25826 * @cfg {String} msgFx @hide
25829 * @cfg {String} validateOnBlur @hide
25833 Roo.HtmlEditorCore.white = [
25834 'area', 'br', 'img', 'input', 'hr', 'wbr',
25836 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
25837 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
25838 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
25839 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
25840 'table', 'ul', 'xmp',
25842 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
25845 'dir', 'menu', 'ol', 'ul', 'dl',
25851 Roo.HtmlEditorCore.black = [
25852 // 'embed', 'object', // enable - backend responsiblity to clean thiese
25854 'base', 'basefont', 'bgsound', 'blink', 'body',
25855 'frame', 'frameset', 'head', 'html', 'ilayer',
25856 'iframe', 'layer', 'link', 'meta', 'object',
25857 'script', 'style' ,'title', 'xml' // clean later..
25859 Roo.HtmlEditorCore.clean = [
25860 'script', 'style', 'title', 'xml'
25862 Roo.HtmlEditorCore.remove = [
25867 Roo.HtmlEditorCore.ablack = [
25871 Roo.HtmlEditorCore.aclean = [
25872 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25876 Roo.HtmlEditorCore.pwhite= [
25877 'http', 'https', 'mailto'
25880 // white listed style attributes.
25881 Roo.HtmlEditorCore.cwhite= [
25882 // 'text-align', /// default is to allow most things..
25888 // black listed style attributes.
25889 Roo.HtmlEditorCore.cblack= [
25890 // 'font-size' -- this can be set by the project
25894 Roo.HtmlEditorCore.swapCodes =[
25895 [ 8211, "–" ],
25896 [ 8212, "—" ],
25913 * @class Roo.bootstrap.HtmlEditor
25914 * @extends Roo.bootstrap.TextArea
25915 * Bootstrap HtmlEditor class
25918 * Create a new HtmlEditor
25919 * @param {Object} config The config object
25922 Roo.bootstrap.HtmlEditor = function(config){
25923 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25924 if (!this.toolbars) {
25925 this.toolbars = [];
25928 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25931 * @event initialize
25932 * Fires when the editor is fully initialized (including the iframe)
25933 * @param {HtmlEditor} this
25938 * Fires when the editor is first receives the focus. Any insertion must wait
25939 * until after this event.
25940 * @param {HtmlEditor} this
25944 * @event beforesync
25945 * Fires before the textarea is updated with content from the editor iframe. Return false
25946 * to cancel the sync.
25947 * @param {HtmlEditor} this
25948 * @param {String} html
25952 * @event beforepush
25953 * Fires before the iframe editor is updated with content from the textarea. Return false
25954 * to cancel the push.
25955 * @param {HtmlEditor} this
25956 * @param {String} html
25961 * Fires when the textarea is updated with content from the editor iframe.
25962 * @param {HtmlEditor} this
25963 * @param {String} html
25968 * Fires when the iframe editor is updated with content from the textarea.
25969 * @param {HtmlEditor} this
25970 * @param {String} html
25974 * @event editmodechange
25975 * Fires when the editor switches edit modes
25976 * @param {HtmlEditor} this
25977 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25979 editmodechange: true,
25981 * @event editorevent
25982 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25983 * @param {HtmlEditor} this
25987 * @event firstfocus
25988 * Fires when on first focus - needed by toolbars..
25989 * @param {HtmlEditor} this
25994 * Auto save the htmlEditor value as a file into Events
25995 * @param {HtmlEditor} this
25999 * @event savedpreview
26000 * preview the saved version of htmlEditor
26001 * @param {HtmlEditor} this
26008 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26012 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26017 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26022 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26027 * @cfg {Number} height (in pixels)
26031 * @cfg {Number} width (in pixels)
26036 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26039 stylesheets: false,
26044 // private properties
26045 validationEvent : false,
26047 initialized : false,
26050 onFocus : Roo.emptyFn,
26052 hideMode:'offsets',
26054 tbContainer : false,
26058 toolbarContainer :function() {
26059 return this.wrap.select('.x-html-editor-tb',true).first();
26063 * Protected method that will not generally be called directly. It
26064 * is called when the editor creates its toolbar. Override this method if you need to
26065 * add custom toolbar buttons.
26066 * @param {HtmlEditor} editor
26068 createToolbar : function(){
26069 Roo.log('renewing');
26070 Roo.log("create toolbars");
26072 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26073 this.toolbars[0].render(this.toolbarContainer());
26077 // if (!editor.toolbars || !editor.toolbars.length) {
26078 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26081 // for (var i =0 ; i < editor.toolbars.length;i++) {
26082 // editor.toolbars[i] = Roo.factory(
26083 // typeof(editor.toolbars[i]) == 'string' ?
26084 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26085 // Roo.bootstrap.HtmlEditor);
26086 // editor.toolbars[i].init(editor);
26092 onRender : function(ct, position)
26094 // Roo.log("Call onRender: " + this.xtype);
26096 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26098 this.wrap = this.inputEl().wrap({
26099 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26102 this.editorcore.onRender(ct, position);
26104 if (this.resizable) {
26105 this.resizeEl = new Roo.Resizable(this.wrap, {
26109 minHeight : this.height,
26110 height: this.height,
26111 handles : this.resizable,
26114 resize : function(r, w, h) {
26115 _t.onResize(w,h); // -something
26121 this.createToolbar(this);
26124 if(!this.width && this.resizable){
26125 this.setSize(this.wrap.getSize());
26127 if (this.resizeEl) {
26128 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26129 // should trigger onReize..
26135 onResize : function(w, h)
26137 Roo.log('resize: ' +w + ',' + h );
26138 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26142 if(this.inputEl() ){
26143 if(typeof w == 'number'){
26144 var aw = w - this.wrap.getFrameWidth('lr');
26145 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26148 if(typeof h == 'number'){
26149 var tbh = -11; // fixme it needs to tool bar size!
26150 for (var i =0; i < this.toolbars.length;i++) {
26151 // fixme - ask toolbars for heights?
26152 tbh += this.toolbars[i].el.getHeight();
26153 //if (this.toolbars[i].footer) {
26154 // tbh += this.toolbars[i].footer.el.getHeight();
26162 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26163 ah -= 5; // knock a few pixes off for look..
26164 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26168 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26169 this.editorcore.onResize(ew,eh);
26174 * Toggles the editor between standard and source edit mode.
26175 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26177 toggleSourceEdit : function(sourceEditMode)
26179 this.editorcore.toggleSourceEdit(sourceEditMode);
26181 if(this.editorcore.sourceEditMode){
26182 Roo.log('editor - showing textarea');
26185 // Roo.log(this.syncValue());
26187 this.inputEl().removeClass(['hide', 'x-hidden']);
26188 this.inputEl().dom.removeAttribute('tabIndex');
26189 this.inputEl().focus();
26191 Roo.log('editor - hiding textarea');
26193 // Roo.log(this.pushValue());
26196 this.inputEl().addClass(['hide', 'x-hidden']);
26197 this.inputEl().dom.setAttribute('tabIndex', -1);
26198 //this.deferFocus();
26201 if(this.resizable){
26202 this.setSize(this.wrap.getSize());
26205 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26208 // private (for BoxComponent)
26209 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26211 // private (for BoxComponent)
26212 getResizeEl : function(){
26216 // private (for BoxComponent)
26217 getPositionEl : function(){
26222 initEvents : function(){
26223 this.originalValue = this.getValue();
26227 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26230 // markInvalid : Roo.emptyFn,
26232 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26235 // clearInvalid : Roo.emptyFn,
26237 setValue : function(v){
26238 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26239 this.editorcore.pushValue();
26244 deferFocus : function(){
26245 this.focus.defer(10, this);
26249 focus : function(){
26250 this.editorcore.focus();
26256 onDestroy : function(){
26262 for (var i =0; i < this.toolbars.length;i++) {
26263 // fixme - ask toolbars for heights?
26264 this.toolbars[i].onDestroy();
26267 this.wrap.dom.innerHTML = '';
26268 this.wrap.remove();
26273 onFirstFocus : function(){
26274 //Roo.log("onFirstFocus");
26275 this.editorcore.onFirstFocus();
26276 for (var i =0; i < this.toolbars.length;i++) {
26277 this.toolbars[i].onFirstFocus();
26283 syncValue : function()
26285 this.editorcore.syncValue();
26288 pushValue : function()
26290 this.editorcore.pushValue();
26294 // hide stuff that is not compatible
26308 * @event specialkey
26312 * @cfg {String} fieldClass @hide
26315 * @cfg {String} focusClass @hide
26318 * @cfg {String} autoCreate @hide
26321 * @cfg {String} inputType @hide
26325 * @cfg {String} invalidText @hide
26328 * @cfg {String} msgFx @hide
26331 * @cfg {String} validateOnBlur @hide
26340 Roo.namespace('Roo.bootstrap.htmleditor');
26342 * @class Roo.bootstrap.HtmlEditorToolbar1
26348 new Roo.bootstrap.HtmlEditor({
26351 new Roo.bootstrap.HtmlEditorToolbar1({
26352 disable : { fonts: 1 , format: 1, ..., ... , ...],
26358 * @cfg {Object} disable List of elements to disable..
26359 * @cfg {Array} btns List of additional buttons.
26363 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26366 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26369 Roo.apply(this, config);
26371 // default disabled, based on 'good practice'..
26372 this.disable = this.disable || {};
26373 Roo.applyIf(this.disable, {
26376 specialElements : true
26378 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26380 this.editor = config.editor;
26381 this.editorcore = config.editor.editorcore;
26383 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26385 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26386 // dont call parent... till later.
26388 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26393 editorcore : false,
26398 "h1","h2","h3","h4","h5","h6",
26400 "abbr", "acronym", "address", "cite", "samp", "var",
26404 onRender : function(ct, position)
26406 // Roo.log("Call onRender: " + this.xtype);
26408 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26410 this.el.dom.style.marginBottom = '0';
26412 var editorcore = this.editorcore;
26413 var editor= this.editor;
26416 var btn = function(id,cmd , toggle, handler, html){
26418 var event = toggle ? 'toggle' : 'click';
26423 xns: Roo.bootstrap,
26427 enableToggle:toggle !== false,
26429 pressed : toggle ? false : null,
26432 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26433 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26439 // var cb_box = function...
26444 xns: Roo.bootstrap,
26449 xns: Roo.bootstrap,
26453 Roo.each(this.formats, function(f) {
26454 style.menu.items.push({
26456 xns: Roo.bootstrap,
26457 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26462 editorcore.insertTag(this.tagname);
26469 children.push(style);
26471 btn('bold',false,true);
26472 btn('italic',false,true);
26473 btn('align-left', 'justifyleft',true);
26474 btn('align-center', 'justifycenter',true);
26475 btn('align-right' , 'justifyright',true);
26476 btn('link', false, false, function(btn) {
26477 //Roo.log("create link?");
26478 var url = prompt(this.createLinkText, this.defaultLinkValue);
26479 if(url && url != 'http:/'+'/'){
26480 this.editorcore.relayCmd('createlink', url);
26483 btn('list','insertunorderedlist',true);
26484 btn('pencil', false,true, function(btn){
26486 this.toggleSourceEdit(btn.pressed);
26489 if (this.editor.btns.length > 0) {
26490 for (var i = 0; i<this.editor.btns.length; i++) {
26491 children.push(this.editor.btns[i]);
26499 xns: Roo.bootstrap,
26504 xns: Roo.bootstrap,
26509 cog.menu.items.push({
26511 xns: Roo.bootstrap,
26512 html : Clean styles,
26517 editorcore.insertTag(this.tagname);
26526 this.xtype = 'NavSimplebar';
26528 for(var i=0;i< children.length;i++) {
26530 this.buttons.add(this.addxtypeChild(children[i]));
26534 editor.on('editorevent', this.updateToolbar, this);
26536 onBtnClick : function(id)
26538 this.editorcore.relayCmd(id);
26539 this.editorcore.focus();
26543 * Protected method that will not generally be called directly. It triggers
26544 * a toolbar update by reading the markup state of the current selection in the editor.
26546 updateToolbar: function(){
26548 if(!this.editorcore.activated){
26549 this.editor.onFirstFocus(); // is this neeed?
26553 var btns = this.buttons;
26554 var doc = this.editorcore.doc;
26555 btns.get('bold').setActive(doc.queryCommandState('bold'));
26556 btns.get('italic').setActive(doc.queryCommandState('italic'));
26557 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26559 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26560 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26561 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26563 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26564 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26567 var ans = this.editorcore.getAllAncestors();
26568 if (this.formatCombo) {
26571 var store = this.formatCombo.store;
26572 this.formatCombo.setValue("");
26573 for (var i =0; i < ans.length;i++) {
26574 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26576 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26584 // hides menus... - so this cant be on a menu...
26585 Roo.bootstrap.MenuMgr.hideAll();
26587 Roo.bootstrap.MenuMgr.hideAll();
26588 //this.editorsyncValue();
26590 onFirstFocus: function() {
26591 this.buttons.each(function(item){
26595 toggleSourceEdit : function(sourceEditMode){
26598 if(sourceEditMode){
26599 Roo.log("disabling buttons");
26600 this.buttons.each( function(item){
26601 if(item.cmd != 'pencil'){
26607 Roo.log("enabling buttons");
26608 if(this.editorcore.initialized){
26609 this.buttons.each( function(item){
26615 Roo.log("calling toggole on editor");
26616 // tell the editor that it's been pressed..
26617 this.editor.toggleSourceEdit(sourceEditMode);
26631 * @class Roo.bootstrap.Markdown
26632 * @extends Roo.bootstrap.TextArea
26633 * Bootstrap Showdown editable area
26634 * @cfg {string} content
26637 * Create a new Showdown
26640 Roo.bootstrap.Markdown = function(config){
26641 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26645 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26649 initEvents : function()
26652 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26653 this.markdownEl = this.el.createChild({
26654 cls : 'roo-markdown-area'
26656 this.inputEl().addClass('d-none');
26657 if (this.getValue() == '') {
26658 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26661 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26663 this.markdownEl.on('click', this.toggleTextEdit, this);
26664 this.on('blur', this.toggleTextEdit, this);
26665 this.on('specialkey', this.resizeTextArea, this);
26668 toggleTextEdit : function()
26670 var sh = this.markdownEl.getHeight();
26671 this.inputEl().addClass('d-none');
26672 this.markdownEl.addClass('d-none');
26673 if (!this.editing) {
26675 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26676 this.inputEl().removeClass('d-none');
26677 this.inputEl().focus();
26678 this.editing = true;
26681 // show showdown...
26682 this.updateMarkdown();
26683 this.markdownEl.removeClass('d-none');
26684 this.editing = false;
26687 updateMarkdown : function()
26689 if (this.getValue() == '') {
26690 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26694 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26697 resizeTextArea: function () {
26700 Roo.log([sh, this.getValue().split("\n").length * 30]);
26701 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26703 setValue : function(val)
26705 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26706 if (!this.editing) {
26707 this.updateMarkdown();
26713 if (!this.editing) {
26714 this.toggleTextEdit();
26722 * @class Roo.bootstrap.Table.AbstractSelectionModel
26723 * @extends Roo.util.Observable
26724 * Abstract base class for grid SelectionModels. It provides the interface that should be
26725 * implemented by descendant classes. This class should not be directly instantiated.
26728 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26729 this.locked = false;
26730 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26734 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
26735 /** @ignore Called by the grid automatically. Do not call directly. */
26736 init : function(grid){
26742 * Locks the selections.
26745 this.locked = true;
26749 * Unlocks the selections.
26751 unlock : function(){
26752 this.locked = false;
26756 * Returns true if the selections are locked.
26757 * @return {Boolean}
26759 isLocked : function(){
26760 return this.locked;
26764 initEvents : function ()
26770 * @extends Roo.bootstrap.Table.AbstractSelectionModel
26771 * @class Roo.bootstrap.Table.RowSelectionModel
26772 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26773 * It supports multiple selections and keyboard selection/navigation.
26775 * @param {Object} config
26778 Roo.bootstrap.Table.RowSelectionModel = function(config){
26779 Roo.apply(this, config);
26780 this.selections = new Roo.util.MixedCollection(false, function(o){
26785 this.lastActive = false;
26789 * @event selectionchange
26790 * Fires when the selection changes
26791 * @param {SelectionModel} this
26793 "selectionchange" : true,
26795 * @event afterselectionchange
26796 * Fires after the selection changes (eg. by key press or clicking)
26797 * @param {SelectionModel} this
26799 "afterselectionchange" : true,
26801 * @event beforerowselect
26802 * Fires when a row is selected being selected, return false to cancel.
26803 * @param {SelectionModel} this
26804 * @param {Number} rowIndex The selected index
26805 * @param {Boolean} keepExisting False if other selections will be cleared
26807 "beforerowselect" : true,
26810 * Fires when a row is selected.
26811 * @param {SelectionModel} this
26812 * @param {Number} rowIndex The selected index
26813 * @param {Roo.data.Record} r The record
26815 "rowselect" : true,
26817 * @event rowdeselect
26818 * Fires when a row is deselected.
26819 * @param {SelectionModel} this
26820 * @param {Number} rowIndex The selected index
26822 "rowdeselect" : true
26824 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26825 this.locked = false;
26828 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
26830 * @cfg {Boolean} singleSelect
26831 * True to allow selection of only one row at a time (defaults to false)
26833 singleSelect : false,
26836 initEvents : function()
26839 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26840 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
26841 //}else{ // allow click to work like normal
26842 // this.grid.on("rowclick", this.handleDragableRowClick, this);
26844 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26845 this.grid.on("rowclick", this.handleMouseDown, this);
26847 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26848 "up" : function(e){
26850 this.selectPrevious(e.shiftKey);
26851 }else if(this.last !== false && this.lastActive !== false){
26852 var last = this.last;
26853 this.selectRange(this.last, this.lastActive-1);
26854 this.grid.getView().focusRow(this.lastActive);
26855 if(last !== false){
26859 this.selectFirstRow();
26861 this.fireEvent("afterselectionchange", this);
26863 "down" : function(e){
26865 this.selectNext(e.shiftKey);
26866 }else if(this.last !== false && this.lastActive !== false){
26867 var last = this.last;
26868 this.selectRange(this.last, this.lastActive+1);
26869 this.grid.getView().focusRow(this.lastActive);
26870 if(last !== false){
26874 this.selectFirstRow();
26876 this.fireEvent("afterselectionchange", this);
26880 this.grid.store.on('load', function(){
26881 this.selections.clear();
26884 var view = this.grid.view;
26885 view.on("refresh", this.onRefresh, this);
26886 view.on("rowupdated", this.onRowUpdated, this);
26887 view.on("rowremoved", this.onRemove, this);
26892 onRefresh : function()
26894 var ds = this.grid.store, i, v = this.grid.view;
26895 var s = this.selections;
26896 s.each(function(r){
26897 if((i = ds.indexOfId(r.id)) != -1){
26906 onRemove : function(v, index, r){
26907 this.selections.remove(r);
26911 onRowUpdated : function(v, index, r){
26912 if(this.isSelected(r)){
26913 v.onRowSelect(index);
26919 * @param {Array} records The records to select
26920 * @param {Boolean} keepExisting (optional) True to keep existing selections
26922 selectRecords : function(records, keepExisting)
26925 this.clearSelections();
26927 var ds = this.grid.store;
26928 for(var i = 0, len = records.length; i < len; i++){
26929 this.selectRow(ds.indexOf(records[i]), true);
26934 * Gets the number of selected rows.
26937 getCount : function(){
26938 return this.selections.length;
26942 * Selects the first row in the grid.
26944 selectFirstRow : function(){
26949 * Select the last row.
26950 * @param {Boolean} keepExisting (optional) True to keep existing selections
26952 selectLastRow : function(keepExisting){
26953 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26954 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26958 * Selects the row immediately following the last selected row.
26959 * @param {Boolean} keepExisting (optional) True to keep existing selections
26961 selectNext : function(keepExisting)
26963 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26964 this.selectRow(this.last+1, keepExisting);
26965 this.grid.getView().focusRow(this.last);
26970 * Selects the row that precedes the last selected row.
26971 * @param {Boolean} keepExisting (optional) True to keep existing selections
26973 selectPrevious : function(keepExisting){
26975 this.selectRow(this.last-1, keepExisting);
26976 this.grid.getView().focusRow(this.last);
26981 * Returns the selected records
26982 * @return {Array} Array of selected records
26984 getSelections : function(){
26985 return [].concat(this.selections.items);
26989 * Returns the first selected record.
26992 getSelected : function(){
26993 return this.selections.itemAt(0);
26998 * Clears all selections.
27000 clearSelections : function(fast)
27006 var ds = this.grid.store;
27007 var s = this.selections;
27008 s.each(function(r){
27009 this.deselectRow(ds.indexOfId(r.id));
27013 this.selections.clear();
27020 * Selects all rows.
27022 selectAll : function(){
27026 this.selections.clear();
27027 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27028 this.selectRow(i, true);
27033 * Returns True if there is a selection.
27034 * @return {Boolean}
27036 hasSelection : function(){
27037 return this.selections.length > 0;
27041 * Returns True if the specified row is selected.
27042 * @param {Number/Record} record The record or index of the record to check
27043 * @return {Boolean}
27045 isSelected : function(index){
27046 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27047 return (r && this.selections.key(r.id) ? true : false);
27051 * Returns True if the specified record id is selected.
27052 * @param {String} id The id of record to check
27053 * @return {Boolean}
27055 isIdSelected : function(id){
27056 return (this.selections.key(id) ? true : false);
27061 handleMouseDBClick : function(e, t){
27065 handleMouseDown : function(e, t)
27067 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27068 if(this.isLocked() || rowIndex < 0 ){
27071 if(e.shiftKey && this.last !== false){
27072 var last = this.last;
27073 this.selectRange(last, rowIndex, e.ctrlKey);
27074 this.last = last; // reset the last
27078 var isSelected = this.isSelected(rowIndex);
27079 //Roo.log("select row:" + rowIndex);
27081 this.deselectRow(rowIndex);
27083 this.selectRow(rowIndex, true);
27087 if(e.button !== 0 && isSelected){
27088 alert('rowIndex 2: ' + rowIndex);
27089 view.focusRow(rowIndex);
27090 }else if(e.ctrlKey && isSelected){
27091 this.deselectRow(rowIndex);
27092 }else if(!isSelected){
27093 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27094 view.focusRow(rowIndex);
27098 this.fireEvent("afterselectionchange", this);
27101 handleDragableRowClick : function(grid, rowIndex, e)
27103 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27104 this.selectRow(rowIndex, false);
27105 grid.view.focusRow(rowIndex);
27106 this.fireEvent("afterselectionchange", this);
27111 * Selects multiple rows.
27112 * @param {Array} rows Array of the indexes of the row to select
27113 * @param {Boolean} keepExisting (optional) True to keep existing selections
27115 selectRows : function(rows, keepExisting){
27117 this.clearSelections();
27119 for(var i = 0, len = rows.length; i < len; i++){
27120 this.selectRow(rows[i], true);
27125 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27126 * @param {Number} startRow The index of the first row in the range
27127 * @param {Number} endRow The index of the last row in the range
27128 * @param {Boolean} keepExisting (optional) True to retain existing selections
27130 selectRange : function(startRow, endRow, keepExisting){
27135 this.clearSelections();
27137 if(startRow <= endRow){
27138 for(var i = startRow; i <= endRow; i++){
27139 this.selectRow(i, true);
27142 for(var i = startRow; i >= endRow; i--){
27143 this.selectRow(i, true);
27149 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27150 * @param {Number} startRow The index of the first row in the range
27151 * @param {Number} endRow The index of the last row in the range
27153 deselectRange : function(startRow, endRow, preventViewNotify){
27157 for(var i = startRow; i <= endRow; i++){
27158 this.deselectRow(i, preventViewNotify);
27164 * @param {Number} row The index of the row to select
27165 * @param {Boolean} keepExisting (optional) True to keep existing selections
27167 selectRow : function(index, keepExisting, preventViewNotify)
27169 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27172 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27173 if(!keepExisting || this.singleSelect){
27174 this.clearSelections();
27177 var r = this.grid.store.getAt(index);
27178 //console.log('selectRow - record id :' + r.id);
27180 this.selections.add(r);
27181 this.last = this.lastActive = index;
27182 if(!preventViewNotify){
27183 var proxy = new Roo.Element(
27184 this.grid.getRowDom(index)
27186 proxy.addClass('bg-info info');
27188 this.fireEvent("rowselect", this, index, r);
27189 this.fireEvent("selectionchange", this);
27195 * @param {Number} row The index of the row to deselect
27197 deselectRow : function(index, preventViewNotify)
27202 if(this.last == index){
27205 if(this.lastActive == index){
27206 this.lastActive = false;
27209 var r = this.grid.store.getAt(index);
27214 this.selections.remove(r);
27215 //.console.log('deselectRow - record id :' + r.id);
27216 if(!preventViewNotify){
27218 var proxy = new Roo.Element(
27219 this.grid.getRowDom(index)
27221 proxy.removeClass('bg-info info');
27223 this.fireEvent("rowdeselect", this, index);
27224 this.fireEvent("selectionchange", this);
27228 restoreLast : function(){
27230 this.last = this._last;
27235 acceptsNav : function(row, col, cm){
27236 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27240 onEditorKey : function(field, e){
27241 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27246 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27248 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27250 }else if(k == e.ENTER && !e.ctrlKey){
27254 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27256 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27258 }else if(k == e.ESC){
27262 g.startEditing(newCell[0], newCell[1]);
27268 * Ext JS Library 1.1.1
27269 * Copyright(c) 2006-2007, Ext JS, LLC.
27271 * Originally Released Under LGPL - original licence link has changed is not relivant.
27274 * <script type="text/javascript">
27278 * @class Roo.bootstrap.PagingToolbar
27279 * @extends Roo.bootstrap.NavSimplebar
27280 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27282 * Create a new PagingToolbar
27283 * @param {Object} config The config object
27284 * @param {Roo.data.Store} store
27286 Roo.bootstrap.PagingToolbar = function(config)
27288 // old args format still supported... - xtype is prefered..
27289 // created from xtype...
27291 this.ds = config.dataSource;
27293 if (config.store && !this.ds) {
27294 this.store= Roo.factory(config.store, Roo.data);
27295 this.ds = this.store;
27296 this.ds.xmodule = this.xmodule || false;
27299 this.toolbarItems = [];
27300 if (config.items) {
27301 this.toolbarItems = config.items;
27304 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27309 this.bind(this.ds);
27312 if (Roo.bootstrap.version == 4) {
27313 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27315 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27320 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27322 * @cfg {Roo.data.Store} dataSource
27323 * The underlying data store providing the paged data
27326 * @cfg {String/HTMLElement/Element} container
27327 * container The id or element that will contain the toolbar
27330 * @cfg {Boolean} displayInfo
27331 * True to display the displayMsg (defaults to false)
27334 * @cfg {Number} pageSize
27335 * The number of records to display per page (defaults to 20)
27339 * @cfg {String} displayMsg
27340 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27342 displayMsg : 'Displaying {0} - {1} of {2}',
27344 * @cfg {String} emptyMsg
27345 * The message to display when no records are found (defaults to "No data to display")
27347 emptyMsg : 'No data to display',
27349 * Customizable piece of the default paging text (defaults to "Page")
27352 beforePageText : "Page",
27354 * Customizable piece of the default paging text (defaults to "of %0")
27357 afterPageText : "of {0}",
27359 * Customizable piece of the default paging text (defaults to "First Page")
27362 firstText : "First Page",
27364 * Customizable piece of the default paging text (defaults to "Previous Page")
27367 prevText : "Previous Page",
27369 * Customizable piece of the default paging text (defaults to "Next Page")
27372 nextText : "Next Page",
27374 * Customizable piece of the default paging text (defaults to "Last Page")
27377 lastText : "Last Page",
27379 * Customizable piece of the default paging text (defaults to "Refresh")
27382 refreshText : "Refresh",
27386 onRender : function(ct, position)
27388 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27389 this.navgroup.parentId = this.id;
27390 this.navgroup.onRender(this.el, null);
27391 // add the buttons to the navgroup
27393 if(this.displayInfo){
27394 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27395 this.displayEl = this.el.select('.x-paging-info', true).first();
27396 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27397 // this.displayEl = navel.el.select('span',true).first();
27403 Roo.each(_this.buttons, function(e){ // this might need to use render????
27404 Roo.factory(e).render(_this.el);
27408 Roo.each(_this.toolbarItems, function(e) {
27409 _this.navgroup.addItem(e);
27413 this.first = this.navgroup.addItem({
27414 tooltip: this.firstText,
27415 cls: "prev btn-outline-secondary",
27416 html : ' <i class="fa fa-step-backward"></i>',
27418 preventDefault: true,
27419 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27422 this.prev = this.navgroup.addItem({
27423 tooltip: this.prevText,
27424 cls: "prev btn-outline-secondary",
27425 html : ' <i class="fa fa-backward"></i>',
27427 preventDefault: true,
27428 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27430 //this.addSeparator();
27433 var field = this.navgroup.addItem( {
27435 cls : 'x-paging-position btn-outline-secondary',
27437 html : this.beforePageText +
27438 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27439 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27442 this.field = field.el.select('input', true).first();
27443 this.field.on("keydown", this.onPagingKeydown, this);
27444 this.field.on("focus", function(){this.dom.select();});
27447 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27448 //this.field.setHeight(18);
27449 //this.addSeparator();
27450 this.next = this.navgroup.addItem({
27451 tooltip: this.nextText,
27452 cls: "next btn-outline-secondary",
27453 html : ' <i class="fa fa-forward"></i>',
27455 preventDefault: true,
27456 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27458 this.last = this.navgroup.addItem({
27459 tooltip: this.lastText,
27460 html : ' <i class="fa fa-step-forward"></i>',
27461 cls: "next btn-outline-secondary",
27463 preventDefault: true,
27464 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27466 //this.addSeparator();
27467 this.loading = this.navgroup.addItem({
27468 tooltip: this.refreshText,
27469 cls: "btn-outline-secondary",
27470 html : ' <i class="fa fa-refresh"></i>',
27471 preventDefault: true,
27472 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27478 updateInfo : function(){
27479 if(this.displayEl){
27480 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27481 var msg = count == 0 ?
27485 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27487 this.displayEl.update(msg);
27492 onLoad : function(ds, r, o)
27494 this.cursor = o.params && o.params.start ? o.params.start : 0;
27496 var d = this.getPageData(),
27501 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27502 this.field.dom.value = ap;
27503 this.first.setDisabled(ap == 1);
27504 this.prev.setDisabled(ap == 1);
27505 this.next.setDisabled(ap == ps);
27506 this.last.setDisabled(ap == ps);
27507 this.loading.enable();
27512 getPageData : function(){
27513 var total = this.ds.getTotalCount();
27516 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27517 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27522 onLoadError : function(){
27523 this.loading.enable();
27527 onPagingKeydown : function(e){
27528 var k = e.getKey();
27529 var d = this.getPageData();
27531 var v = this.field.dom.value, pageNum;
27532 if(!v || isNaN(pageNum = parseInt(v, 10))){
27533 this.field.dom.value = d.activePage;
27536 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27537 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27540 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))
27542 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27543 this.field.dom.value = pageNum;
27544 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27547 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27549 var v = this.field.dom.value, pageNum;
27550 var increment = (e.shiftKey) ? 10 : 1;
27551 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27554 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27555 this.field.dom.value = d.activePage;
27558 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27560 this.field.dom.value = parseInt(v, 10) + increment;
27561 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27562 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27569 beforeLoad : function(){
27571 this.loading.disable();
27576 onClick : function(which){
27585 ds.load({params:{start: 0, limit: this.pageSize}});
27588 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27591 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27594 var total = ds.getTotalCount();
27595 var extra = total % this.pageSize;
27596 var lastStart = extra ? (total - extra) : total-this.pageSize;
27597 ds.load({params:{start: lastStart, limit: this.pageSize}});
27600 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27606 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27607 * @param {Roo.data.Store} store The data store to unbind
27609 unbind : function(ds){
27610 ds.un("beforeload", this.beforeLoad, this);
27611 ds.un("load", this.onLoad, this);
27612 ds.un("loadexception", this.onLoadError, this);
27613 ds.un("remove", this.updateInfo, this);
27614 ds.un("add", this.updateInfo, this);
27615 this.ds = undefined;
27619 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27620 * @param {Roo.data.Store} store The data store to bind
27622 bind : function(ds){
27623 ds.on("beforeload", this.beforeLoad, this);
27624 ds.on("load", this.onLoad, this);
27625 ds.on("loadexception", this.onLoadError, this);
27626 ds.on("remove", this.updateInfo, this);
27627 ds.on("add", this.updateInfo, this);
27638 * @class Roo.bootstrap.MessageBar
27639 * @extends Roo.bootstrap.Component
27640 * Bootstrap MessageBar class
27641 * @cfg {String} html contents of the MessageBar
27642 * @cfg {String} weight (info | success | warning | danger) default info
27643 * @cfg {String} beforeClass insert the bar before the given class
27644 * @cfg {Boolean} closable (true | false) default false
27645 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27648 * Create a new Element
27649 * @param {Object} config The config object
27652 Roo.bootstrap.MessageBar = function(config){
27653 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27656 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27662 beforeClass: 'bootstrap-sticky-wrap',
27664 getAutoCreate : function(){
27668 cls: 'alert alert-dismissable alert-' + this.weight,
27673 html: this.html || ''
27679 cfg.cls += ' alert-messages-fixed';
27693 onRender : function(ct, position)
27695 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27698 var cfg = Roo.apply({}, this.getAutoCreate());
27702 cfg.cls += ' ' + this.cls;
27705 cfg.style = this.style;
27707 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27709 this.el.setVisibilityMode(Roo.Element.DISPLAY);
27712 this.el.select('>button.close').on('click', this.hide, this);
27718 if (!this.rendered) {
27724 this.fireEvent('show', this);
27730 if (!this.rendered) {
27736 this.fireEvent('hide', this);
27739 update : function()
27741 // var e = this.el.dom.firstChild;
27743 // if(this.closable){
27744 // e = e.nextSibling;
27747 // e.data = this.html || '';
27749 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27765 * @class Roo.bootstrap.Graph
27766 * @extends Roo.bootstrap.Component
27767 * Bootstrap Graph class
27771 @cfg {String} graphtype bar | vbar | pie
27772 @cfg {number} g_x coodinator | centre x (pie)
27773 @cfg {number} g_y coodinator | centre y (pie)
27774 @cfg {number} g_r radius (pie)
27775 @cfg {number} g_height height of the chart (respected by all elements in the set)
27776 @cfg {number} g_width width of the chart (respected by all elements in the set)
27777 @cfg {Object} title The title of the chart
27780 -opts (object) options for the chart
27782 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27783 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27785 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.
27786 o stacked (boolean) whether or not to tread values as in a stacked bar chart
27788 o stretch (boolean)
27790 -opts (object) options for the pie
27793 o startAngle (number)
27794 o endAngle (number)
27798 * Create a new Input
27799 * @param {Object} config The config object
27802 Roo.bootstrap.Graph = function(config){
27803 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27809 * The img click event for the img.
27810 * @param {Roo.EventObject} e
27816 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
27827 //g_colors: this.colors,
27834 getAutoCreate : function(){
27845 onRender : function(ct,position){
27848 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27850 if (typeof(Raphael) == 'undefined') {
27851 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27855 this.raphael = Raphael(this.el.dom);
27857 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27858 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27859 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27860 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27862 r.text(160, 10, "Single Series Chart").attr(txtattr);
27863 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27864 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27865 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27867 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27868 r.barchart(330, 10, 300, 220, data1);
27869 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27870 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27873 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27874 // r.barchart(30, 30, 560, 250, xdata, {
27875 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27876 // axis : "0 0 1 1",
27877 // axisxlabels : xdata
27878 // //yvalues : cols,
27881 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27883 // this.load(null,xdata,{
27884 // axis : "0 0 1 1",
27885 // axisxlabels : xdata
27890 load : function(graphtype,xdata,opts)
27892 this.raphael.clear();
27894 graphtype = this.graphtype;
27899 var r = this.raphael,
27900 fin = function () {
27901 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27903 fout = function () {
27904 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27906 pfin = function() {
27907 this.sector.stop();
27908 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27911 this.label[0].stop();
27912 this.label[0].attr({ r: 7.5 });
27913 this.label[1].attr({ "font-weight": 800 });
27916 pfout = function() {
27917 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27920 this.label[0].animate({ r: 5 }, 500, "bounce");
27921 this.label[1].attr({ "font-weight": 400 });
27927 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27930 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27933 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
27934 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27936 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27943 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27948 setTitle: function(o)
27953 initEvents: function() {
27956 this.el.on('click', this.onClick, this);
27960 onClick : function(e)
27962 Roo.log('img onclick');
27963 this.fireEvent('click', this, e);
27975 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27978 * @class Roo.bootstrap.dash.NumberBox
27979 * @extends Roo.bootstrap.Component
27980 * Bootstrap NumberBox class
27981 * @cfg {String} headline Box headline
27982 * @cfg {String} content Box content
27983 * @cfg {String} icon Box icon
27984 * @cfg {String} footer Footer text
27985 * @cfg {String} fhref Footer href
27988 * Create a new NumberBox
27989 * @param {Object} config The config object
27993 Roo.bootstrap.dash.NumberBox = function(config){
27994 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27998 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28007 getAutoCreate : function(){
28011 cls : 'small-box ',
28019 cls : 'roo-headline',
28020 html : this.headline
28024 cls : 'roo-content',
28025 html : this.content
28039 cls : 'ion ' + this.icon
28048 cls : 'small-box-footer',
28049 href : this.fhref || '#',
28053 cfg.cn.push(footer);
28060 onRender : function(ct,position){
28061 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28068 setHeadline: function (value)
28070 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28073 setFooter: function (value, href)
28075 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28078 this.el.select('a.small-box-footer',true).first().attr('href', href);
28083 setContent: function (value)
28085 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28088 initEvents: function()
28102 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28105 * @class Roo.bootstrap.dash.TabBox
28106 * @extends Roo.bootstrap.Component
28107 * Bootstrap TabBox class
28108 * @cfg {String} title Title of the TabBox
28109 * @cfg {String} icon Icon of the TabBox
28110 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28111 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28114 * Create a new TabBox
28115 * @param {Object} config The config object
28119 Roo.bootstrap.dash.TabBox = function(config){
28120 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28125 * When a pane is added
28126 * @param {Roo.bootstrap.dash.TabPane} pane
28130 * @event activatepane
28131 * When a pane is activated
28132 * @param {Roo.bootstrap.dash.TabPane} pane
28134 "activatepane" : true
28142 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28147 tabScrollable : false,
28149 getChildContainer : function()
28151 return this.el.select('.tab-content', true).first();
28154 getAutoCreate : function(){
28158 cls: 'pull-left header',
28166 cls: 'fa ' + this.icon
28172 cls: 'nav nav-tabs pull-right',
28178 if(this.tabScrollable){
28185 cls: 'nav nav-tabs pull-right',
28196 cls: 'nav-tabs-custom',
28201 cls: 'tab-content no-padding',
28209 initEvents : function()
28211 //Roo.log('add add pane handler');
28212 this.on('addpane', this.onAddPane, this);
28215 * Updates the box title
28216 * @param {String} html to set the title to.
28218 setTitle : function(value)
28220 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28222 onAddPane : function(pane)
28224 this.panes.push(pane);
28225 //Roo.log('addpane');
28227 // tabs are rendere left to right..
28228 if(!this.showtabs){
28232 var ctr = this.el.select('.nav-tabs', true).first();
28235 var existing = ctr.select('.nav-tab',true);
28236 var qty = existing.getCount();;
28239 var tab = ctr.createChild({
28241 cls : 'nav-tab' + (qty ? '' : ' active'),
28249 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28252 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28254 pane.el.addClass('active');
28259 onTabClick : function(ev,un,ob,pane)
28261 //Roo.log('tab - prev default');
28262 ev.preventDefault();
28265 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28266 pane.tab.addClass('active');
28267 //Roo.log(pane.title);
28268 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28269 // technically we should have a deactivate event.. but maybe add later.
28270 // and it should not de-activate the selected tab...
28271 this.fireEvent('activatepane', pane);
28272 pane.el.addClass('active');
28273 pane.fireEvent('activate');
28278 getActivePane : function()
28281 Roo.each(this.panes, function(p) {
28282 if(p.el.hasClass('active')){
28303 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28305 * @class Roo.bootstrap.TabPane
28306 * @extends Roo.bootstrap.Component
28307 * Bootstrap TabPane class
28308 * @cfg {Boolean} active (false | true) Default false
28309 * @cfg {String} title title of panel
28313 * Create a new TabPane
28314 * @param {Object} config The config object
28317 Roo.bootstrap.dash.TabPane = function(config){
28318 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28324 * When a pane is activated
28325 * @param {Roo.bootstrap.dash.TabPane} pane
28332 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28337 // the tabBox that this is attached to.
28340 getAutoCreate : function()
28348 cfg.cls += ' active';
28353 initEvents : function()
28355 //Roo.log('trigger add pane handler');
28356 this.parent().fireEvent('addpane', this)
28360 * Updates the tab title
28361 * @param {String} html to set the title to.
28363 setTitle: function(str)
28369 this.tab.select('a', true).first().dom.innerHTML = str;
28386 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28389 * @class Roo.bootstrap.menu.Menu
28390 * @extends Roo.bootstrap.Component
28391 * Bootstrap Menu class - container for Menu
28392 * @cfg {String} html Text of the menu
28393 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28394 * @cfg {String} icon Font awesome icon
28395 * @cfg {String} pos Menu align to (top | bottom) default bottom
28399 * Create a new Menu
28400 * @param {Object} config The config object
28404 Roo.bootstrap.menu.Menu = function(config){
28405 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28409 * @event beforeshow
28410 * Fires before this menu is displayed
28411 * @param {Roo.bootstrap.menu.Menu} this
28415 * @event beforehide
28416 * Fires before this menu is hidden
28417 * @param {Roo.bootstrap.menu.Menu} this
28422 * Fires after this menu is displayed
28423 * @param {Roo.bootstrap.menu.Menu} this
28428 * Fires after this menu is hidden
28429 * @param {Roo.bootstrap.menu.Menu} this
28434 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28435 * @param {Roo.bootstrap.menu.Menu} this
28436 * @param {Roo.EventObject} e
28443 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28447 weight : 'default',
28452 getChildContainer : function() {
28453 if(this.isSubMenu){
28457 return this.el.select('ul.dropdown-menu', true).first();
28460 getAutoCreate : function()
28465 cls : 'roo-menu-text',
28473 cls : 'fa ' + this.icon
28484 cls : 'dropdown-button btn btn-' + this.weight,
28489 cls : 'dropdown-toggle btn btn-' + this.weight,
28499 cls : 'dropdown-menu'
28505 if(this.pos == 'top'){
28506 cfg.cls += ' dropup';
28509 if(this.isSubMenu){
28512 cls : 'dropdown-menu'
28519 onRender : function(ct, position)
28521 this.isSubMenu = ct.hasClass('dropdown-submenu');
28523 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28526 initEvents : function()
28528 if(this.isSubMenu){
28532 this.hidden = true;
28534 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28535 this.triggerEl.on('click', this.onTriggerPress, this);
28537 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28538 this.buttonEl.on('click', this.onClick, this);
28544 if(this.isSubMenu){
28548 return this.el.select('ul.dropdown-menu', true).first();
28551 onClick : function(e)
28553 this.fireEvent("click", this, e);
28556 onTriggerPress : function(e)
28558 if (this.isVisible()) {
28565 isVisible : function(){
28566 return !this.hidden;
28571 this.fireEvent("beforeshow", this);
28573 this.hidden = false;
28574 this.el.addClass('open');
28576 Roo.get(document).on("mouseup", this.onMouseUp, this);
28578 this.fireEvent("show", this);
28585 this.fireEvent("beforehide", this);
28587 this.hidden = true;
28588 this.el.removeClass('open');
28590 Roo.get(document).un("mouseup", this.onMouseUp);
28592 this.fireEvent("hide", this);
28595 onMouseUp : function()
28609 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28612 * @class Roo.bootstrap.menu.Item
28613 * @extends Roo.bootstrap.Component
28614 * Bootstrap MenuItem class
28615 * @cfg {Boolean} submenu (true | false) default false
28616 * @cfg {String} html text of the item
28617 * @cfg {String} href the link
28618 * @cfg {Boolean} disable (true | false) default false
28619 * @cfg {Boolean} preventDefault (true | false) default true
28620 * @cfg {String} icon Font awesome icon
28621 * @cfg {String} pos Submenu align to (left | right) default right
28625 * Create a new Item
28626 * @param {Object} config The config object
28630 Roo.bootstrap.menu.Item = function(config){
28631 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28635 * Fires when the mouse is hovering over this menu
28636 * @param {Roo.bootstrap.menu.Item} this
28637 * @param {Roo.EventObject} e
28642 * Fires when the mouse exits this menu
28643 * @param {Roo.bootstrap.menu.Item} this
28644 * @param {Roo.EventObject} e
28650 * The raw click event for the entire grid.
28651 * @param {Roo.EventObject} e
28657 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28662 preventDefault: true,
28667 getAutoCreate : function()
28672 cls : 'roo-menu-item-text',
28680 cls : 'fa ' + this.icon
28689 href : this.href || '#',
28696 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28700 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28702 if(this.pos == 'left'){
28703 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28710 initEvents : function()
28712 this.el.on('mouseover', this.onMouseOver, this);
28713 this.el.on('mouseout', this.onMouseOut, this);
28715 this.el.select('a', true).first().on('click', this.onClick, this);
28719 onClick : function(e)
28721 if(this.preventDefault){
28722 e.preventDefault();
28725 this.fireEvent("click", this, e);
28728 onMouseOver : function(e)
28730 if(this.submenu && this.pos == 'left'){
28731 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28734 this.fireEvent("mouseover", this, e);
28737 onMouseOut : function(e)
28739 this.fireEvent("mouseout", this, e);
28751 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28754 * @class Roo.bootstrap.menu.Separator
28755 * @extends Roo.bootstrap.Component
28756 * Bootstrap Separator class
28759 * Create a new Separator
28760 * @param {Object} config The config object
28764 Roo.bootstrap.menu.Separator = function(config){
28765 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28768 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
28770 getAutoCreate : function(){
28791 * @class Roo.bootstrap.Tooltip
28792 * Bootstrap Tooltip class
28793 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28794 * to determine which dom element triggers the tooltip.
28796 * It needs to add support for additional attributes like tooltip-position
28799 * Create a new Toolti
28800 * @param {Object} config The config object
28803 Roo.bootstrap.Tooltip = function(config){
28804 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28806 this.alignment = Roo.bootstrap.Tooltip.alignment;
28808 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28809 this.alignment = config.alignment;
28814 Roo.apply(Roo.bootstrap.Tooltip, {
28816 * @function init initialize tooltip monitoring.
28820 currentTip : false,
28821 currentRegion : false,
28827 Roo.get(document).on('mouseover', this.enter ,this);
28828 Roo.get(document).on('mouseout', this.leave, this);
28831 this.currentTip = new Roo.bootstrap.Tooltip();
28834 enter : function(ev)
28836 var dom = ev.getTarget();
28838 //Roo.log(['enter',dom]);
28839 var el = Roo.fly(dom);
28840 if (this.currentEl) {
28842 //Roo.log(this.currentEl);
28843 //Roo.log(this.currentEl.contains(dom));
28844 if (this.currentEl == el) {
28847 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28853 if (this.currentTip.el) {
28854 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28858 if(!el || el.dom == document){
28864 // you can not look for children, as if el is the body.. then everythign is the child..
28865 if (!el.attr('tooltip')) { //
28866 if (!el.select("[tooltip]").elements.length) {
28869 // is the mouse over this child...?
28870 bindEl = el.select("[tooltip]").first();
28871 var xy = ev.getXY();
28872 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28873 //Roo.log("not in region.");
28876 //Roo.log("child element over..");
28879 this.currentEl = bindEl;
28880 this.currentTip.bind(bindEl);
28881 this.currentRegion = Roo.lib.Region.getRegion(dom);
28882 this.currentTip.enter();
28885 leave : function(ev)
28887 var dom = ev.getTarget();
28888 //Roo.log(['leave',dom]);
28889 if (!this.currentEl) {
28894 if (dom != this.currentEl.dom) {
28897 var xy = ev.getXY();
28898 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
28901 // only activate leave if mouse cursor is outside... bounding box..
28906 if (this.currentTip) {
28907 this.currentTip.leave();
28909 //Roo.log('clear currentEl');
28910 this.currentEl = false;
28915 'left' : ['r-l', [-2,0], 'right'],
28916 'right' : ['l-r', [2,0], 'left'],
28917 'bottom' : ['t-b', [0,2], 'top'],
28918 'top' : [ 'b-t', [0,-2], 'bottom']
28924 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
28929 delay : null, // can be { show : 300 , hide: 500}
28933 hoverState : null, //???
28935 placement : 'bottom',
28939 getAutoCreate : function(){
28946 cls : 'tooltip-arrow arrow'
28949 cls : 'tooltip-inner'
28956 bind : function(el)
28961 initEvents : function()
28963 this.arrowEl = this.el.select('.arrow', true).first();
28964 this.innerEl = this.el.select('.tooltip-inner', true).first();
28967 enter : function () {
28969 if (this.timeout != null) {
28970 clearTimeout(this.timeout);
28973 this.hoverState = 'in';
28974 //Roo.log("enter - show");
28975 if (!this.delay || !this.delay.show) {
28980 this.timeout = setTimeout(function () {
28981 if (_t.hoverState == 'in') {
28984 }, this.delay.show);
28988 clearTimeout(this.timeout);
28990 this.hoverState = 'out';
28991 if (!this.delay || !this.delay.hide) {
28997 this.timeout = setTimeout(function () {
28998 //Roo.log("leave - timeout");
29000 if (_t.hoverState == 'out') {
29002 Roo.bootstrap.Tooltip.currentEl = false;
29007 show : function (msg)
29010 this.render(document.body);
29013 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29015 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29017 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29019 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29020 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29022 var placement = typeof this.placement == 'function' ?
29023 this.placement.call(this, this.el, on_el) :
29026 var autoToken = /\s?auto?\s?/i;
29027 var autoPlace = autoToken.test(placement);
29029 placement = placement.replace(autoToken, '') || 'top';
29033 //this.el.setXY([0,0]);
29035 //this.el.dom.style.display='block';
29037 //this.el.appendTo(on_el);
29039 var p = this.getPosition();
29040 var box = this.el.getBox();
29046 var align = this.alignment[placement];
29048 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29050 if(placement == 'top' || placement == 'bottom'){
29052 placement = 'right';
29055 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29056 placement = 'left';
29059 var scroll = Roo.select('body', true).first().getScroll();
29061 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29065 align = this.alignment[placement];
29067 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29071 this.el.alignTo(this.bindEl, align[0],align[1]);
29072 //var arrow = this.el.select('.arrow',true).first();
29073 //arrow.set(align[2],
29075 this.el.addClass(placement);
29076 this.el.addClass("bs-tooltip-"+ placement);
29078 this.el.addClass('in fade show');
29080 this.hoverState = null;
29082 if (this.el.hasClass('fade')) {
29097 //this.el.setXY([0,0]);
29098 this.el.removeClass(['show', 'in']);
29114 * @class Roo.bootstrap.LocationPicker
29115 * @extends Roo.bootstrap.Component
29116 * Bootstrap LocationPicker class
29117 * @cfg {Number} latitude Position when init default 0
29118 * @cfg {Number} longitude Position when init default 0
29119 * @cfg {Number} zoom default 15
29120 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29121 * @cfg {Boolean} mapTypeControl default false
29122 * @cfg {Boolean} disableDoubleClickZoom default false
29123 * @cfg {Boolean} scrollwheel default true
29124 * @cfg {Boolean} streetViewControl default false
29125 * @cfg {Number} radius default 0
29126 * @cfg {String} locationName
29127 * @cfg {Boolean} draggable default true
29128 * @cfg {Boolean} enableAutocomplete default false
29129 * @cfg {Boolean} enableReverseGeocode default true
29130 * @cfg {String} markerTitle
29133 * Create a new LocationPicker
29134 * @param {Object} config The config object
29138 Roo.bootstrap.LocationPicker = function(config){
29140 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29145 * Fires when the picker initialized.
29146 * @param {Roo.bootstrap.LocationPicker} this
29147 * @param {Google Location} location
29151 * @event positionchanged
29152 * Fires when the picker position changed.
29153 * @param {Roo.bootstrap.LocationPicker} this
29154 * @param {Google Location} location
29156 positionchanged : true,
29159 * Fires when the map resize.
29160 * @param {Roo.bootstrap.LocationPicker} this
29165 * Fires when the map show.
29166 * @param {Roo.bootstrap.LocationPicker} this
29171 * Fires when the map hide.
29172 * @param {Roo.bootstrap.LocationPicker} this
29177 * Fires when click the map.
29178 * @param {Roo.bootstrap.LocationPicker} this
29179 * @param {Map event} e
29183 * @event mapRightClick
29184 * Fires when right click the map.
29185 * @param {Roo.bootstrap.LocationPicker} this
29186 * @param {Map event} e
29188 mapRightClick : true,
29190 * @event markerClick
29191 * Fires when click the marker.
29192 * @param {Roo.bootstrap.LocationPicker} this
29193 * @param {Map event} e
29195 markerClick : true,
29197 * @event markerRightClick
29198 * Fires when right click the marker.
29199 * @param {Roo.bootstrap.LocationPicker} this
29200 * @param {Map event} e
29202 markerRightClick : true,
29204 * @event OverlayViewDraw
29205 * Fires when OverlayView Draw
29206 * @param {Roo.bootstrap.LocationPicker} this
29208 OverlayViewDraw : true,
29210 * @event OverlayViewOnAdd
29211 * Fires when OverlayView Draw
29212 * @param {Roo.bootstrap.LocationPicker} this
29214 OverlayViewOnAdd : true,
29216 * @event OverlayViewOnRemove
29217 * Fires when OverlayView Draw
29218 * @param {Roo.bootstrap.LocationPicker} this
29220 OverlayViewOnRemove : true,
29222 * @event OverlayViewShow
29223 * Fires when OverlayView Draw
29224 * @param {Roo.bootstrap.LocationPicker} this
29225 * @param {Pixel} cpx
29227 OverlayViewShow : true,
29229 * @event OverlayViewHide
29230 * Fires when OverlayView Draw
29231 * @param {Roo.bootstrap.LocationPicker} this
29233 OverlayViewHide : true,
29235 * @event loadexception
29236 * Fires when load google lib failed.
29237 * @param {Roo.bootstrap.LocationPicker} this
29239 loadexception : true
29244 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29246 gMapContext: false,
29252 mapTypeControl: false,
29253 disableDoubleClickZoom: false,
29255 streetViewControl: false,
29259 enableAutocomplete: false,
29260 enableReverseGeocode: true,
29263 getAutoCreate: function()
29268 cls: 'roo-location-picker'
29274 initEvents: function(ct, position)
29276 if(!this.el.getWidth() || this.isApplied()){
29280 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29285 initial: function()
29287 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29288 this.fireEvent('loadexception', this);
29292 if(!this.mapTypeId){
29293 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29296 this.gMapContext = this.GMapContext();
29298 this.initOverlayView();
29300 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29304 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29305 _this.setPosition(_this.gMapContext.marker.position);
29308 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29309 _this.fireEvent('mapClick', this, event);
29313 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29314 _this.fireEvent('mapRightClick', this, event);
29318 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29319 _this.fireEvent('markerClick', this, event);
29323 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29324 _this.fireEvent('markerRightClick', this, event);
29328 this.setPosition(this.gMapContext.location);
29330 this.fireEvent('initial', this, this.gMapContext.location);
29333 initOverlayView: function()
29337 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29341 _this.fireEvent('OverlayViewDraw', _this);
29346 _this.fireEvent('OverlayViewOnAdd', _this);
29349 onRemove: function()
29351 _this.fireEvent('OverlayViewOnRemove', _this);
29354 show: function(cpx)
29356 _this.fireEvent('OverlayViewShow', _this, cpx);
29361 _this.fireEvent('OverlayViewHide', _this);
29367 fromLatLngToContainerPixel: function(event)
29369 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29372 isApplied: function()
29374 return this.getGmapContext() == false ? false : true;
29377 getGmapContext: function()
29379 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29382 GMapContext: function()
29384 var position = new google.maps.LatLng(this.latitude, this.longitude);
29386 var _map = new google.maps.Map(this.el.dom, {
29389 mapTypeId: this.mapTypeId,
29390 mapTypeControl: this.mapTypeControl,
29391 disableDoubleClickZoom: this.disableDoubleClickZoom,
29392 scrollwheel: this.scrollwheel,
29393 streetViewControl: this.streetViewControl,
29394 locationName: this.locationName,
29395 draggable: this.draggable,
29396 enableAutocomplete: this.enableAutocomplete,
29397 enableReverseGeocode: this.enableReverseGeocode
29400 var _marker = new google.maps.Marker({
29401 position: position,
29403 title: this.markerTitle,
29404 draggable: this.draggable
29411 location: position,
29412 radius: this.radius,
29413 locationName: this.locationName,
29414 addressComponents: {
29415 formatted_address: null,
29416 addressLine1: null,
29417 addressLine2: null,
29419 streetNumber: null,
29423 stateOrProvince: null
29426 domContainer: this.el.dom,
29427 geodecoder: new google.maps.Geocoder()
29431 drawCircle: function(center, radius, options)
29433 if (this.gMapContext.circle != null) {
29434 this.gMapContext.circle.setMap(null);
29438 options = Roo.apply({}, options, {
29439 strokeColor: "#0000FF",
29440 strokeOpacity: .35,
29442 fillColor: "#0000FF",
29446 options.map = this.gMapContext.map;
29447 options.radius = radius;
29448 options.center = center;
29449 this.gMapContext.circle = new google.maps.Circle(options);
29450 return this.gMapContext.circle;
29456 setPosition: function(location)
29458 this.gMapContext.location = location;
29459 this.gMapContext.marker.setPosition(location);
29460 this.gMapContext.map.panTo(location);
29461 this.drawCircle(location, this.gMapContext.radius, {});
29465 if (this.gMapContext.settings.enableReverseGeocode) {
29466 this.gMapContext.geodecoder.geocode({
29467 latLng: this.gMapContext.location
29468 }, function(results, status) {
29470 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29471 _this.gMapContext.locationName = results[0].formatted_address;
29472 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29474 _this.fireEvent('positionchanged', this, location);
29481 this.fireEvent('positionchanged', this, location);
29486 google.maps.event.trigger(this.gMapContext.map, "resize");
29488 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29490 this.fireEvent('resize', this);
29493 setPositionByLatLng: function(latitude, longitude)
29495 this.setPosition(new google.maps.LatLng(latitude, longitude));
29498 getCurrentPosition: function()
29501 latitude: this.gMapContext.location.lat(),
29502 longitude: this.gMapContext.location.lng()
29506 getAddressName: function()
29508 return this.gMapContext.locationName;
29511 getAddressComponents: function()
29513 return this.gMapContext.addressComponents;
29516 address_component_from_google_geocode: function(address_components)
29520 for (var i = 0; i < address_components.length; i++) {
29521 var component = address_components[i];
29522 if (component.types.indexOf("postal_code") >= 0) {
29523 result.postalCode = component.short_name;
29524 } else if (component.types.indexOf("street_number") >= 0) {
29525 result.streetNumber = component.short_name;
29526 } else if (component.types.indexOf("route") >= 0) {
29527 result.streetName = component.short_name;
29528 } else if (component.types.indexOf("neighborhood") >= 0) {
29529 result.city = component.short_name;
29530 } else if (component.types.indexOf("locality") >= 0) {
29531 result.city = component.short_name;
29532 } else if (component.types.indexOf("sublocality") >= 0) {
29533 result.district = component.short_name;
29534 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29535 result.stateOrProvince = component.short_name;
29536 } else if (component.types.indexOf("country") >= 0) {
29537 result.country = component.short_name;
29541 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29542 result.addressLine2 = "";
29546 setZoomLevel: function(zoom)
29548 this.gMapContext.map.setZoom(zoom);
29561 this.fireEvent('show', this);
29572 this.fireEvent('hide', this);
29577 Roo.apply(Roo.bootstrap.LocationPicker, {
29579 OverlayView : function(map, options)
29581 options = options || {};
29588 * @class Roo.bootstrap.Alert
29589 * @extends Roo.bootstrap.Component
29590 * Bootstrap Alert class - shows an alert area box
29592 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29593 Enter a valid email address
29596 * @cfg {String} title The title of alert
29597 * @cfg {String} html The content of alert
29598 * @cfg {String} weight ( success | info | warning | danger )
29599 * @cfg {String} faicon font-awesomeicon
29602 * Create a new alert
29603 * @param {Object} config The config object
29607 Roo.bootstrap.Alert = function(config){
29608 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29612 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29619 getAutoCreate : function()
29628 cls : 'roo-alert-icon'
29633 cls : 'roo-alert-title',
29638 cls : 'roo-alert-text',
29645 cfg.cn[0].cls += ' fa ' + this.faicon;
29649 cfg.cls += ' alert-' + this.weight;
29655 initEvents: function()
29657 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29660 setTitle : function(str)
29662 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29665 setText : function(str)
29667 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29670 setWeight : function(weight)
29673 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29676 this.weight = weight;
29678 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29681 setIcon : function(icon)
29684 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29687 this.faicon = icon;
29689 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29710 * @class Roo.bootstrap.UploadCropbox
29711 * @extends Roo.bootstrap.Component
29712 * Bootstrap UploadCropbox class
29713 * @cfg {String} emptyText show when image has been loaded
29714 * @cfg {String} rotateNotify show when image too small to rotate
29715 * @cfg {Number} errorTimeout default 3000
29716 * @cfg {Number} minWidth default 300
29717 * @cfg {Number} minHeight default 300
29718 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29719 * @cfg {Boolean} isDocument (true|false) default false
29720 * @cfg {String} url action url
29721 * @cfg {String} paramName default 'imageUpload'
29722 * @cfg {String} method default POST
29723 * @cfg {Boolean} loadMask (true|false) default true
29724 * @cfg {Boolean} loadingText default 'Loading...'
29727 * Create a new UploadCropbox
29728 * @param {Object} config The config object
29731 Roo.bootstrap.UploadCropbox = function(config){
29732 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29736 * @event beforeselectfile
29737 * Fire before select file
29738 * @param {Roo.bootstrap.UploadCropbox} this
29740 "beforeselectfile" : true,
29743 * Fire after initEvent
29744 * @param {Roo.bootstrap.UploadCropbox} this
29749 * Fire after initEvent
29750 * @param {Roo.bootstrap.UploadCropbox} this
29751 * @param {String} data
29756 * Fire when preparing the file data
29757 * @param {Roo.bootstrap.UploadCropbox} this
29758 * @param {Object} file
29763 * Fire when get exception
29764 * @param {Roo.bootstrap.UploadCropbox} this
29765 * @param {XMLHttpRequest} xhr
29767 "exception" : true,
29769 * @event beforeloadcanvas
29770 * Fire before load the canvas
29771 * @param {Roo.bootstrap.UploadCropbox} this
29772 * @param {String} src
29774 "beforeloadcanvas" : true,
29777 * Fire when trash image
29778 * @param {Roo.bootstrap.UploadCropbox} this
29783 * Fire when download the image
29784 * @param {Roo.bootstrap.UploadCropbox} this
29788 * @event footerbuttonclick
29789 * Fire when footerbuttonclick
29790 * @param {Roo.bootstrap.UploadCropbox} this
29791 * @param {String} type
29793 "footerbuttonclick" : true,
29797 * @param {Roo.bootstrap.UploadCropbox} this
29802 * Fire when rotate the image
29803 * @param {Roo.bootstrap.UploadCropbox} this
29804 * @param {String} pos
29809 * Fire when inspect the file
29810 * @param {Roo.bootstrap.UploadCropbox} this
29811 * @param {Object} file
29816 * Fire when xhr upload the file
29817 * @param {Roo.bootstrap.UploadCropbox} this
29818 * @param {Object} data
29823 * Fire when arrange the file data
29824 * @param {Roo.bootstrap.UploadCropbox} this
29825 * @param {Object} formData
29830 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29833 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
29835 emptyText : 'Click to upload image',
29836 rotateNotify : 'Image is too small to rotate',
29837 errorTimeout : 3000,
29851 cropType : 'image/jpeg',
29853 canvasLoaded : false,
29854 isDocument : false,
29856 paramName : 'imageUpload',
29858 loadingText : 'Loading...',
29861 getAutoCreate : function()
29865 cls : 'roo-upload-cropbox',
29869 cls : 'roo-upload-cropbox-selector',
29874 cls : 'roo-upload-cropbox-body',
29875 style : 'cursor:pointer',
29879 cls : 'roo-upload-cropbox-preview'
29883 cls : 'roo-upload-cropbox-thumb'
29887 cls : 'roo-upload-cropbox-empty-notify',
29888 html : this.emptyText
29892 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29893 html : this.rotateNotify
29899 cls : 'roo-upload-cropbox-footer',
29902 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29912 onRender : function(ct, position)
29914 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29916 if (this.buttons.length) {
29918 Roo.each(this.buttons, function(bb) {
29920 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29922 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29928 this.maskEl = this.el;
29932 initEvents : function()
29934 this.urlAPI = (window.createObjectURL && window) ||
29935 (window.URL && URL.revokeObjectURL && URL) ||
29936 (window.webkitURL && webkitURL);
29938 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29939 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29941 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29942 this.selectorEl.hide();
29944 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29945 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29947 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29948 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29949 this.thumbEl.hide();
29951 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29952 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29954 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29955 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29956 this.errorEl.hide();
29958 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29959 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29960 this.footerEl.hide();
29962 this.setThumbBoxSize();
29968 this.fireEvent('initial', this);
29975 window.addEventListener("resize", function() { _this.resize(); } );
29977 this.bodyEl.on('click', this.beforeSelectFile, this);
29980 this.bodyEl.on('touchstart', this.onTouchStart, this);
29981 this.bodyEl.on('touchmove', this.onTouchMove, this);
29982 this.bodyEl.on('touchend', this.onTouchEnd, this);
29986 this.bodyEl.on('mousedown', this.onMouseDown, this);
29987 this.bodyEl.on('mousemove', this.onMouseMove, this);
29988 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29989 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29990 Roo.get(document).on('mouseup', this.onMouseUp, this);
29993 this.selectorEl.on('change', this.onFileSelected, this);
29999 this.baseScale = 1;
30001 this.baseRotate = 1;
30002 this.dragable = false;
30003 this.pinching = false;
30006 this.cropData = false;
30007 this.notifyEl.dom.innerHTML = this.emptyText;
30009 this.selectorEl.dom.value = '';
30013 resize : function()
30015 if(this.fireEvent('resize', this) != false){
30016 this.setThumbBoxPosition();
30017 this.setCanvasPosition();
30021 onFooterButtonClick : function(e, el, o, type)
30024 case 'rotate-left' :
30025 this.onRotateLeft(e);
30027 case 'rotate-right' :
30028 this.onRotateRight(e);
30031 this.beforeSelectFile(e);
30046 this.fireEvent('footerbuttonclick', this, type);
30049 beforeSelectFile : function(e)
30051 e.preventDefault();
30053 if(this.fireEvent('beforeselectfile', this) != false){
30054 this.selectorEl.dom.click();
30058 onFileSelected : function(e)
30060 e.preventDefault();
30062 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30066 var file = this.selectorEl.dom.files[0];
30068 if(this.fireEvent('inspect', this, file) != false){
30069 this.prepare(file);
30074 trash : function(e)
30076 this.fireEvent('trash', this);
30079 download : function(e)
30081 this.fireEvent('download', this);
30084 loadCanvas : function(src)
30086 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30090 this.imageEl = document.createElement('img');
30094 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30096 this.imageEl.src = src;
30100 onLoadCanvas : function()
30102 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30103 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30105 this.bodyEl.un('click', this.beforeSelectFile, this);
30107 this.notifyEl.hide();
30108 this.thumbEl.show();
30109 this.footerEl.show();
30111 this.baseRotateLevel();
30113 if(this.isDocument){
30114 this.setThumbBoxSize();
30117 this.setThumbBoxPosition();
30119 this.baseScaleLevel();
30125 this.canvasLoaded = true;
30128 this.maskEl.unmask();
30133 setCanvasPosition : function()
30135 if(!this.canvasEl){
30139 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30140 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30142 this.previewEl.setLeft(pw);
30143 this.previewEl.setTop(ph);
30147 onMouseDown : function(e)
30151 this.dragable = true;
30152 this.pinching = false;
30154 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30155 this.dragable = false;
30159 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30160 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30164 onMouseMove : function(e)
30168 if(!this.canvasLoaded){
30172 if (!this.dragable){
30176 var minX = Math.ceil(this.thumbEl.getLeft(true));
30177 var minY = Math.ceil(this.thumbEl.getTop(true));
30179 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30180 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30182 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30183 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30185 x = x - this.mouseX;
30186 y = y - this.mouseY;
30188 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30189 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30191 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30192 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30194 this.previewEl.setLeft(bgX);
30195 this.previewEl.setTop(bgY);
30197 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30198 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30201 onMouseUp : function(e)
30205 this.dragable = false;
30208 onMouseWheel : function(e)
30212 this.startScale = this.scale;
30214 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30216 if(!this.zoomable()){
30217 this.scale = this.startScale;
30226 zoomable : function()
30228 var minScale = this.thumbEl.getWidth() / this.minWidth;
30230 if(this.minWidth < this.minHeight){
30231 minScale = this.thumbEl.getHeight() / this.minHeight;
30234 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30235 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30239 (this.rotate == 0 || this.rotate == 180) &&
30241 width > this.imageEl.OriginWidth ||
30242 height > this.imageEl.OriginHeight ||
30243 (width < this.minWidth && height < this.minHeight)
30251 (this.rotate == 90 || this.rotate == 270) &&
30253 width > this.imageEl.OriginWidth ||
30254 height > this.imageEl.OriginHeight ||
30255 (width < this.minHeight && height < this.minWidth)
30262 !this.isDocument &&
30263 (this.rotate == 0 || this.rotate == 180) &&
30265 width < this.minWidth ||
30266 width > this.imageEl.OriginWidth ||
30267 height < this.minHeight ||
30268 height > this.imageEl.OriginHeight
30275 !this.isDocument &&
30276 (this.rotate == 90 || this.rotate == 270) &&
30278 width < this.minHeight ||
30279 width > this.imageEl.OriginWidth ||
30280 height < this.minWidth ||
30281 height > this.imageEl.OriginHeight
30291 onRotateLeft : function(e)
30293 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30295 var minScale = this.thumbEl.getWidth() / this.minWidth;
30297 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30298 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30300 this.startScale = this.scale;
30302 while (this.getScaleLevel() < minScale){
30304 this.scale = this.scale + 1;
30306 if(!this.zoomable()){
30311 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30312 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30317 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30324 this.scale = this.startScale;
30326 this.onRotateFail();
30331 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30333 if(this.isDocument){
30334 this.setThumbBoxSize();
30335 this.setThumbBoxPosition();
30336 this.setCanvasPosition();
30341 this.fireEvent('rotate', this, 'left');
30345 onRotateRight : function(e)
30347 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30349 var minScale = this.thumbEl.getWidth() / this.minWidth;
30351 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30352 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30354 this.startScale = this.scale;
30356 while (this.getScaleLevel() < minScale){
30358 this.scale = this.scale + 1;
30360 if(!this.zoomable()){
30365 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30366 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30371 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30378 this.scale = this.startScale;
30380 this.onRotateFail();
30385 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30387 if(this.isDocument){
30388 this.setThumbBoxSize();
30389 this.setThumbBoxPosition();
30390 this.setCanvasPosition();
30395 this.fireEvent('rotate', this, 'right');
30398 onRotateFail : function()
30400 this.errorEl.show(true);
30404 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30409 this.previewEl.dom.innerHTML = '';
30411 var canvasEl = document.createElement("canvas");
30413 var contextEl = canvasEl.getContext("2d");
30415 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30416 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30417 var center = this.imageEl.OriginWidth / 2;
30419 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30420 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30421 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30422 center = this.imageEl.OriginHeight / 2;
30425 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30427 contextEl.translate(center, center);
30428 contextEl.rotate(this.rotate * Math.PI / 180);
30430 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30432 this.canvasEl = document.createElement("canvas");
30434 this.contextEl = this.canvasEl.getContext("2d");
30436 switch (this.rotate) {
30439 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30440 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30442 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30447 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30448 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30450 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30451 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);
30455 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30460 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30461 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30463 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30464 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);
30468 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);
30473 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30474 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30476 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30477 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30481 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);
30488 this.previewEl.appendChild(this.canvasEl);
30490 this.setCanvasPosition();
30495 if(!this.canvasLoaded){
30499 var imageCanvas = document.createElement("canvas");
30501 var imageContext = imageCanvas.getContext("2d");
30503 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30504 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30506 var center = imageCanvas.width / 2;
30508 imageContext.translate(center, center);
30510 imageContext.rotate(this.rotate * Math.PI / 180);
30512 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30514 var canvas = document.createElement("canvas");
30516 var context = canvas.getContext("2d");
30518 canvas.width = this.minWidth;
30519 canvas.height = this.minHeight;
30521 switch (this.rotate) {
30524 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30525 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30527 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30528 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30530 var targetWidth = this.minWidth - 2 * x;
30531 var targetHeight = this.minHeight - 2 * y;
30535 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30536 scale = targetWidth / width;
30539 if(x > 0 && y == 0){
30540 scale = targetHeight / height;
30543 if(x > 0 && y > 0){
30544 scale = targetWidth / width;
30546 if(width < height){
30547 scale = targetHeight / height;
30551 context.scale(scale, scale);
30553 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30554 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30556 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30557 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30559 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30564 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30565 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30567 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30568 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30570 var targetWidth = this.minWidth - 2 * x;
30571 var targetHeight = this.minHeight - 2 * y;
30575 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30576 scale = targetWidth / width;
30579 if(x > 0 && y == 0){
30580 scale = targetHeight / height;
30583 if(x > 0 && y > 0){
30584 scale = targetWidth / width;
30586 if(width < height){
30587 scale = targetHeight / height;
30591 context.scale(scale, scale);
30593 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30594 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30596 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30597 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30599 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30601 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30606 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30607 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30609 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30610 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30612 var targetWidth = this.minWidth - 2 * x;
30613 var targetHeight = this.minHeight - 2 * y;
30617 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30618 scale = targetWidth / width;
30621 if(x > 0 && y == 0){
30622 scale = targetHeight / height;
30625 if(x > 0 && y > 0){
30626 scale = targetWidth / width;
30628 if(width < height){
30629 scale = targetHeight / height;
30633 context.scale(scale, scale);
30635 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30636 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30638 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30639 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30641 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30642 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30644 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30649 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30650 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30652 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30653 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30655 var targetWidth = this.minWidth - 2 * x;
30656 var targetHeight = this.minHeight - 2 * y;
30660 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30661 scale = targetWidth / width;
30664 if(x > 0 && y == 0){
30665 scale = targetHeight / height;
30668 if(x > 0 && y > 0){
30669 scale = targetWidth / width;
30671 if(width < height){
30672 scale = targetHeight / height;
30676 context.scale(scale, scale);
30678 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30679 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30681 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30682 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30684 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30686 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30693 this.cropData = canvas.toDataURL(this.cropType);
30695 if(this.fireEvent('crop', this, this.cropData) !== false){
30696 this.process(this.file, this.cropData);
30703 setThumbBoxSize : function()
30707 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30708 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30709 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30711 this.minWidth = width;
30712 this.minHeight = height;
30714 if(this.rotate == 90 || this.rotate == 270){
30715 this.minWidth = height;
30716 this.minHeight = width;
30721 width = Math.ceil(this.minWidth * height / this.minHeight);
30723 if(this.minWidth > this.minHeight){
30725 height = Math.ceil(this.minHeight * width / this.minWidth);
30728 this.thumbEl.setStyle({
30729 width : width + 'px',
30730 height : height + 'px'
30737 setThumbBoxPosition : function()
30739 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30740 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30742 this.thumbEl.setLeft(x);
30743 this.thumbEl.setTop(y);
30747 baseRotateLevel : function()
30749 this.baseRotate = 1;
30752 typeof(this.exif) != 'undefined' &&
30753 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30754 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30756 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30759 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30763 baseScaleLevel : function()
30767 if(this.isDocument){
30769 if(this.baseRotate == 6 || this.baseRotate == 8){
30771 height = this.thumbEl.getHeight();
30772 this.baseScale = height / this.imageEl.OriginWidth;
30774 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30775 width = this.thumbEl.getWidth();
30776 this.baseScale = width / this.imageEl.OriginHeight;
30782 height = this.thumbEl.getHeight();
30783 this.baseScale = height / this.imageEl.OriginHeight;
30785 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30786 width = this.thumbEl.getWidth();
30787 this.baseScale = width / this.imageEl.OriginWidth;
30793 if(this.baseRotate == 6 || this.baseRotate == 8){
30795 width = this.thumbEl.getHeight();
30796 this.baseScale = width / this.imageEl.OriginHeight;
30798 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30799 height = this.thumbEl.getWidth();
30800 this.baseScale = height / this.imageEl.OriginHeight;
30803 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30804 height = this.thumbEl.getWidth();
30805 this.baseScale = height / this.imageEl.OriginHeight;
30807 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30808 width = this.thumbEl.getHeight();
30809 this.baseScale = width / this.imageEl.OriginWidth;
30816 width = this.thumbEl.getWidth();
30817 this.baseScale = width / this.imageEl.OriginWidth;
30819 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30820 height = this.thumbEl.getHeight();
30821 this.baseScale = height / this.imageEl.OriginHeight;
30824 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30826 height = this.thumbEl.getHeight();
30827 this.baseScale = height / this.imageEl.OriginHeight;
30829 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30830 width = this.thumbEl.getWidth();
30831 this.baseScale = width / this.imageEl.OriginWidth;
30839 getScaleLevel : function()
30841 return this.baseScale * Math.pow(1.1, this.scale);
30844 onTouchStart : function(e)
30846 if(!this.canvasLoaded){
30847 this.beforeSelectFile(e);
30851 var touches = e.browserEvent.touches;
30857 if(touches.length == 1){
30858 this.onMouseDown(e);
30862 if(touches.length != 2){
30868 for(var i = 0, finger; finger = touches[i]; i++){
30869 coords.push(finger.pageX, finger.pageY);
30872 var x = Math.pow(coords[0] - coords[2], 2);
30873 var y = Math.pow(coords[1] - coords[3], 2);
30875 this.startDistance = Math.sqrt(x + y);
30877 this.startScale = this.scale;
30879 this.pinching = true;
30880 this.dragable = false;
30884 onTouchMove : function(e)
30886 if(!this.pinching && !this.dragable){
30890 var touches = e.browserEvent.touches;
30897 this.onMouseMove(e);
30903 for(var i = 0, finger; finger = touches[i]; i++){
30904 coords.push(finger.pageX, finger.pageY);
30907 var x = Math.pow(coords[0] - coords[2], 2);
30908 var y = Math.pow(coords[1] - coords[3], 2);
30910 this.endDistance = Math.sqrt(x + y);
30912 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30914 if(!this.zoomable()){
30915 this.scale = this.startScale;
30923 onTouchEnd : function(e)
30925 this.pinching = false;
30926 this.dragable = false;
30930 process : function(file, crop)
30933 this.maskEl.mask(this.loadingText);
30936 this.xhr = new XMLHttpRequest();
30938 file.xhr = this.xhr;
30940 this.xhr.open(this.method, this.url, true);
30943 "Accept": "application/json",
30944 "Cache-Control": "no-cache",
30945 "X-Requested-With": "XMLHttpRequest"
30948 for (var headerName in headers) {
30949 var headerValue = headers[headerName];
30951 this.xhr.setRequestHeader(headerName, headerValue);
30957 this.xhr.onload = function()
30959 _this.xhrOnLoad(_this.xhr);
30962 this.xhr.onerror = function()
30964 _this.xhrOnError(_this.xhr);
30967 var formData = new FormData();
30969 formData.append('returnHTML', 'NO');
30972 formData.append('crop', crop);
30975 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30976 formData.append(this.paramName, file, file.name);
30979 if(typeof(file.filename) != 'undefined'){
30980 formData.append('filename', file.filename);
30983 if(typeof(file.mimetype) != 'undefined'){
30984 formData.append('mimetype', file.mimetype);
30987 if(this.fireEvent('arrange', this, formData) != false){
30988 this.xhr.send(formData);
30992 xhrOnLoad : function(xhr)
30995 this.maskEl.unmask();
30998 if (xhr.readyState !== 4) {
30999 this.fireEvent('exception', this, xhr);
31003 var response = Roo.decode(xhr.responseText);
31005 if(!response.success){
31006 this.fireEvent('exception', this, xhr);
31010 var response = Roo.decode(xhr.responseText);
31012 this.fireEvent('upload', this, response);
31016 xhrOnError : function()
31019 this.maskEl.unmask();
31022 Roo.log('xhr on error');
31024 var response = Roo.decode(xhr.responseText);
31030 prepare : function(file)
31033 this.maskEl.mask(this.loadingText);
31039 if(typeof(file) === 'string'){
31040 this.loadCanvas(file);
31044 if(!file || !this.urlAPI){
31049 this.cropType = file.type;
31053 if(this.fireEvent('prepare', this, this.file) != false){
31055 var reader = new FileReader();
31057 reader.onload = function (e) {
31058 if (e.target.error) {
31059 Roo.log(e.target.error);
31063 var buffer = e.target.result,
31064 dataView = new DataView(buffer),
31066 maxOffset = dataView.byteLength - 4,
31070 if (dataView.getUint16(0) === 0xffd8) {
31071 while (offset < maxOffset) {
31072 markerBytes = dataView.getUint16(offset);
31074 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31075 markerLength = dataView.getUint16(offset + 2) + 2;
31076 if (offset + markerLength > dataView.byteLength) {
31077 Roo.log('Invalid meta data: Invalid segment size.');
31081 if(markerBytes == 0xffe1){
31082 _this.parseExifData(
31089 offset += markerLength;
31099 var url = _this.urlAPI.createObjectURL(_this.file);
31101 _this.loadCanvas(url);
31106 reader.readAsArrayBuffer(this.file);
31112 parseExifData : function(dataView, offset, length)
31114 var tiffOffset = offset + 10,
31118 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31119 // No Exif data, might be XMP data instead
31123 // Check for the ASCII code for "Exif" (0x45786966):
31124 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31125 // No Exif data, might be XMP data instead
31128 if (tiffOffset + 8 > dataView.byteLength) {
31129 Roo.log('Invalid Exif data: Invalid segment size.');
31132 // Check for the two null bytes:
31133 if (dataView.getUint16(offset + 8) !== 0x0000) {
31134 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31137 // Check the byte alignment:
31138 switch (dataView.getUint16(tiffOffset)) {
31140 littleEndian = true;
31143 littleEndian = false;
31146 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31149 // Check for the TIFF tag marker (0x002A):
31150 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31151 Roo.log('Invalid Exif data: Missing TIFF marker.');
31154 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31155 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31157 this.parseExifTags(
31160 tiffOffset + dirOffset,
31165 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31170 if (dirOffset + 6 > dataView.byteLength) {
31171 Roo.log('Invalid Exif data: Invalid directory offset.');
31174 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31175 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31176 if (dirEndOffset + 4 > dataView.byteLength) {
31177 Roo.log('Invalid Exif data: Invalid directory size.');
31180 for (i = 0; i < tagsNumber; i += 1) {
31184 dirOffset + 2 + 12 * i, // tag offset
31188 // Return the offset to the next directory:
31189 return dataView.getUint32(dirEndOffset, littleEndian);
31192 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31194 var tag = dataView.getUint16(offset, littleEndian);
31196 this.exif[tag] = this.getExifValue(
31200 dataView.getUint16(offset + 2, littleEndian), // tag type
31201 dataView.getUint32(offset + 4, littleEndian), // tag length
31206 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31208 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31217 Roo.log('Invalid Exif data: Invalid tag type.');
31221 tagSize = tagType.size * length;
31222 // Determine if the value is contained in the dataOffset bytes,
31223 // or if the value at the dataOffset is a pointer to the actual data:
31224 dataOffset = tagSize > 4 ?
31225 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31226 if (dataOffset + tagSize > dataView.byteLength) {
31227 Roo.log('Invalid Exif data: Invalid data offset.');
31230 if (length === 1) {
31231 return tagType.getValue(dataView, dataOffset, littleEndian);
31234 for (i = 0; i < length; i += 1) {
31235 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31238 if (tagType.ascii) {
31240 // Concatenate the chars:
31241 for (i = 0; i < values.length; i += 1) {
31243 // Ignore the terminating NULL byte(s):
31244 if (c === '\u0000') {
31256 Roo.apply(Roo.bootstrap.UploadCropbox, {
31258 'Orientation': 0x0112
31262 1: 0, //'top-left',
31264 3: 180, //'bottom-right',
31265 // 4: 'bottom-left',
31267 6: 90, //'right-top',
31268 // 7: 'right-bottom',
31269 8: 270 //'left-bottom'
31273 // byte, 8-bit unsigned int:
31275 getValue: function (dataView, dataOffset) {
31276 return dataView.getUint8(dataOffset);
31280 // ascii, 8-bit byte:
31282 getValue: function (dataView, dataOffset) {
31283 return String.fromCharCode(dataView.getUint8(dataOffset));
31288 // short, 16 bit int:
31290 getValue: function (dataView, dataOffset, littleEndian) {
31291 return dataView.getUint16(dataOffset, littleEndian);
31295 // long, 32 bit int:
31297 getValue: function (dataView, dataOffset, littleEndian) {
31298 return dataView.getUint32(dataOffset, littleEndian);
31302 // rational = two long values, first is numerator, second is denominator:
31304 getValue: function (dataView, dataOffset, littleEndian) {
31305 return dataView.getUint32(dataOffset, littleEndian) /
31306 dataView.getUint32(dataOffset + 4, littleEndian);
31310 // slong, 32 bit signed int:
31312 getValue: function (dataView, dataOffset, littleEndian) {
31313 return dataView.getInt32(dataOffset, littleEndian);
31317 // srational, two slongs, first is numerator, second is denominator:
31319 getValue: function (dataView, dataOffset, littleEndian) {
31320 return dataView.getInt32(dataOffset, littleEndian) /
31321 dataView.getInt32(dataOffset + 4, littleEndian);
31331 cls : 'btn-group roo-upload-cropbox-rotate-left',
31332 action : 'rotate-left',
31336 cls : 'btn btn-default',
31337 html : '<i class="fa fa-undo"></i>'
31343 cls : 'btn-group roo-upload-cropbox-picture',
31344 action : 'picture',
31348 cls : 'btn btn-default',
31349 html : '<i class="fa fa-picture-o"></i>'
31355 cls : 'btn-group roo-upload-cropbox-rotate-right',
31356 action : 'rotate-right',
31360 cls : 'btn btn-default',
31361 html : '<i class="fa fa-repeat"></i>'
31369 cls : 'btn-group roo-upload-cropbox-rotate-left',
31370 action : 'rotate-left',
31374 cls : 'btn btn-default',
31375 html : '<i class="fa fa-undo"></i>'
31381 cls : 'btn-group roo-upload-cropbox-download',
31382 action : 'download',
31386 cls : 'btn btn-default',
31387 html : '<i class="fa fa-download"></i>'
31393 cls : 'btn-group roo-upload-cropbox-crop',
31398 cls : 'btn btn-default',
31399 html : '<i class="fa fa-crop"></i>'
31405 cls : 'btn-group roo-upload-cropbox-trash',
31410 cls : 'btn btn-default',
31411 html : '<i class="fa fa-trash"></i>'
31417 cls : 'btn-group roo-upload-cropbox-rotate-right',
31418 action : 'rotate-right',
31422 cls : 'btn btn-default',
31423 html : '<i class="fa fa-repeat"></i>'
31431 cls : 'btn-group roo-upload-cropbox-rotate-left',
31432 action : 'rotate-left',
31436 cls : 'btn btn-default',
31437 html : '<i class="fa fa-undo"></i>'
31443 cls : 'btn-group roo-upload-cropbox-rotate-right',
31444 action : 'rotate-right',
31448 cls : 'btn btn-default',
31449 html : '<i class="fa fa-repeat"></i>'
31462 * @class Roo.bootstrap.DocumentManager
31463 * @extends Roo.bootstrap.Component
31464 * Bootstrap DocumentManager class
31465 * @cfg {String} paramName default 'imageUpload'
31466 * @cfg {String} toolTipName default 'filename'
31467 * @cfg {String} method default POST
31468 * @cfg {String} url action url
31469 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31470 * @cfg {Boolean} multiple multiple upload default true
31471 * @cfg {Number} thumbSize default 300
31472 * @cfg {String} fieldLabel
31473 * @cfg {Number} labelWidth default 4
31474 * @cfg {String} labelAlign (left|top) default left
31475 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31476 * @cfg {Number} labellg set the width of label (1-12)
31477 * @cfg {Number} labelmd set the width of label (1-12)
31478 * @cfg {Number} labelsm set the width of label (1-12)
31479 * @cfg {Number} labelxs set the width of label (1-12)
31482 * Create a new DocumentManager
31483 * @param {Object} config The config object
31486 Roo.bootstrap.DocumentManager = function(config){
31487 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31490 this.delegates = [];
31495 * Fire when initial the DocumentManager
31496 * @param {Roo.bootstrap.DocumentManager} this
31501 * inspect selected file
31502 * @param {Roo.bootstrap.DocumentManager} this
31503 * @param {File} file
31508 * Fire when xhr load exception
31509 * @param {Roo.bootstrap.DocumentManager} this
31510 * @param {XMLHttpRequest} xhr
31512 "exception" : true,
31514 * @event afterupload
31515 * Fire when xhr load exception
31516 * @param {Roo.bootstrap.DocumentManager} this
31517 * @param {XMLHttpRequest} xhr
31519 "afterupload" : true,
31522 * prepare the form data
31523 * @param {Roo.bootstrap.DocumentManager} this
31524 * @param {Object} formData
31529 * Fire when remove the file
31530 * @param {Roo.bootstrap.DocumentManager} this
31531 * @param {Object} file
31536 * Fire after refresh the file
31537 * @param {Roo.bootstrap.DocumentManager} this
31542 * Fire after click the image
31543 * @param {Roo.bootstrap.DocumentManager} this
31544 * @param {Object} file
31549 * Fire when upload a image and editable set to true
31550 * @param {Roo.bootstrap.DocumentManager} this
31551 * @param {Object} file
31555 * @event beforeselectfile
31556 * Fire before select file
31557 * @param {Roo.bootstrap.DocumentManager} this
31559 "beforeselectfile" : true,
31562 * Fire before process file
31563 * @param {Roo.bootstrap.DocumentManager} this
31564 * @param {Object} file
31568 * @event previewrendered
31569 * Fire when preview rendered
31570 * @param {Roo.bootstrap.DocumentManager} this
31571 * @param {Object} file
31573 "previewrendered" : true,
31576 "previewResize" : true
31581 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31590 paramName : 'imageUpload',
31591 toolTipName : 'filename',
31594 labelAlign : 'left',
31604 getAutoCreate : function()
31606 var managerWidget = {
31608 cls : 'roo-document-manager',
31612 cls : 'roo-document-manager-selector',
31617 cls : 'roo-document-manager-uploader',
31621 cls : 'roo-document-manager-upload-btn',
31622 html : '<i class="fa fa-plus"></i>'
31633 cls : 'column col-md-12',
31638 if(this.fieldLabel.length){
31643 cls : 'column col-md-12',
31644 html : this.fieldLabel
31648 cls : 'column col-md-12',
31653 if(this.labelAlign == 'left'){
31658 html : this.fieldLabel
31667 if(this.labelWidth > 12){
31668 content[0].style = "width: " + this.labelWidth + 'px';
31671 if(this.labelWidth < 13 && this.labelmd == 0){
31672 this.labelmd = this.labelWidth;
31675 if(this.labellg > 0){
31676 content[0].cls += ' col-lg-' + this.labellg;
31677 content[1].cls += ' col-lg-' + (12 - this.labellg);
31680 if(this.labelmd > 0){
31681 content[0].cls += ' col-md-' + this.labelmd;
31682 content[1].cls += ' col-md-' + (12 - this.labelmd);
31685 if(this.labelsm > 0){
31686 content[0].cls += ' col-sm-' + this.labelsm;
31687 content[1].cls += ' col-sm-' + (12 - this.labelsm);
31690 if(this.labelxs > 0){
31691 content[0].cls += ' col-xs-' + this.labelxs;
31692 content[1].cls += ' col-xs-' + (12 - this.labelxs);
31700 cls : 'row clearfix',
31708 initEvents : function()
31710 this.managerEl = this.el.select('.roo-document-manager', true).first();
31711 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31713 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31714 this.selectorEl.hide();
31717 this.selectorEl.attr('multiple', 'multiple');
31720 this.selectorEl.on('change', this.onFileSelected, this);
31722 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31723 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31725 this.uploader.on('click', this.onUploaderClick, this);
31727 this.renderProgressDialog();
31731 window.addEventListener("resize", function() { _this.refresh(); } );
31733 this.fireEvent('initial', this);
31736 renderProgressDialog : function()
31740 this.progressDialog = new Roo.bootstrap.Modal({
31741 cls : 'roo-document-manager-progress-dialog',
31742 allow_close : false,
31753 btnclick : function() {
31754 _this.uploadCancel();
31760 this.progressDialog.render(Roo.get(document.body));
31762 this.progress = new Roo.bootstrap.Progress({
31763 cls : 'roo-document-manager-progress',
31768 this.progress.render(this.progressDialog.getChildContainer());
31770 this.progressBar = new Roo.bootstrap.ProgressBar({
31771 cls : 'roo-document-manager-progress-bar',
31774 aria_valuemax : 12,
31778 this.progressBar.render(this.progress.getChildContainer());
31781 onUploaderClick : function(e)
31783 e.preventDefault();
31785 if(this.fireEvent('beforeselectfile', this) != false){
31786 this.selectorEl.dom.click();
31791 onFileSelected : function(e)
31793 e.preventDefault();
31795 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31799 Roo.each(this.selectorEl.dom.files, function(file){
31800 if(this.fireEvent('inspect', this, file) != false){
31801 this.files.push(file);
31811 this.selectorEl.dom.value = '';
31813 if(!this.files || !this.files.length){
31817 if(this.boxes > 0 && this.files.length > this.boxes){
31818 this.files = this.files.slice(0, this.boxes);
31821 this.uploader.show();
31823 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31824 this.uploader.hide();
31833 Roo.each(this.files, function(file){
31835 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31836 var f = this.renderPreview(file);
31841 if(file.type.indexOf('image') != -1){
31842 this.delegates.push(
31844 _this.process(file);
31845 }).createDelegate(this)
31853 _this.process(file);
31854 }).createDelegate(this)
31859 this.files = files;
31861 this.delegates = this.delegates.concat(docs);
31863 if(!this.delegates.length){
31868 this.progressBar.aria_valuemax = this.delegates.length;
31875 arrange : function()
31877 if(!this.delegates.length){
31878 this.progressDialog.hide();
31883 var delegate = this.delegates.shift();
31885 this.progressDialog.show();
31887 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31889 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31894 refresh : function()
31896 this.uploader.show();
31898 if(this.boxes > 0 && this.files.length > this.boxes - 1){
31899 this.uploader.hide();
31902 Roo.isTouch ? this.closable(false) : this.closable(true);
31904 this.fireEvent('refresh', this);
31907 onRemove : function(e, el, o)
31909 e.preventDefault();
31911 this.fireEvent('remove', this, o);
31915 remove : function(o)
31919 Roo.each(this.files, function(file){
31920 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31929 this.files = files;
31936 Roo.each(this.files, function(file){
31941 file.target.remove();
31950 onClick : function(e, el, o)
31952 e.preventDefault();
31954 this.fireEvent('click', this, o);
31958 closable : function(closable)
31960 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31962 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31974 xhrOnLoad : function(xhr)
31976 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31980 if (xhr.readyState !== 4) {
31982 this.fireEvent('exception', this, xhr);
31986 var response = Roo.decode(xhr.responseText);
31988 if(!response.success){
31990 this.fireEvent('exception', this, xhr);
31994 var file = this.renderPreview(response.data);
31996 this.files.push(file);
32000 this.fireEvent('afterupload', this, xhr);
32004 xhrOnError : function(xhr)
32006 Roo.log('xhr on error');
32008 var response = Roo.decode(xhr.responseText);
32015 process : function(file)
32017 if(this.fireEvent('process', this, file) !== false){
32018 if(this.editable && file.type.indexOf('image') != -1){
32019 this.fireEvent('edit', this, file);
32023 this.uploadStart(file, false);
32030 uploadStart : function(file, crop)
32032 this.xhr = new XMLHttpRequest();
32034 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32039 file.xhr = this.xhr;
32041 this.managerEl.createChild({
32043 cls : 'roo-document-manager-loading',
32047 tooltip : file.name,
32048 cls : 'roo-document-manager-thumb',
32049 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32055 this.xhr.open(this.method, this.url, true);
32058 "Accept": "application/json",
32059 "Cache-Control": "no-cache",
32060 "X-Requested-With": "XMLHttpRequest"
32063 for (var headerName in headers) {
32064 var headerValue = headers[headerName];
32066 this.xhr.setRequestHeader(headerName, headerValue);
32072 this.xhr.onload = function()
32074 _this.xhrOnLoad(_this.xhr);
32077 this.xhr.onerror = function()
32079 _this.xhrOnError(_this.xhr);
32082 var formData = new FormData();
32084 formData.append('returnHTML', 'NO');
32087 formData.append('crop', crop);
32090 formData.append(this.paramName, file, file.name);
32097 if(this.fireEvent('prepare', this, formData, options) != false){
32099 if(options.manually){
32103 this.xhr.send(formData);
32107 this.uploadCancel();
32110 uploadCancel : function()
32116 this.delegates = [];
32118 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32125 renderPreview : function(file)
32127 if(typeof(file.target) != 'undefined' && file.target){
32131 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32133 var previewEl = this.managerEl.createChild({
32135 cls : 'roo-document-manager-preview',
32139 tooltip : file[this.toolTipName],
32140 cls : 'roo-document-manager-thumb',
32141 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32146 html : '<i class="fa fa-times-circle"></i>'
32151 var close = previewEl.select('button.close', true).first();
32153 close.on('click', this.onRemove, this, file);
32155 file.target = previewEl;
32157 var image = previewEl.select('img', true).first();
32161 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32163 image.on('click', this.onClick, this, file);
32165 this.fireEvent('previewrendered', this, file);
32171 onPreviewLoad : function(file, image)
32173 if(typeof(file.target) == 'undefined' || !file.target){
32177 var width = image.dom.naturalWidth || image.dom.width;
32178 var height = image.dom.naturalHeight || image.dom.height;
32180 if(!this.previewResize) {
32184 if(width > height){
32185 file.target.addClass('wide');
32189 file.target.addClass('tall');
32194 uploadFromSource : function(file, crop)
32196 this.xhr = new XMLHttpRequest();
32198 this.managerEl.createChild({
32200 cls : 'roo-document-manager-loading',
32204 tooltip : file.name,
32205 cls : 'roo-document-manager-thumb',
32206 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32212 this.xhr.open(this.method, this.url, true);
32215 "Accept": "application/json",
32216 "Cache-Control": "no-cache",
32217 "X-Requested-With": "XMLHttpRequest"
32220 for (var headerName in headers) {
32221 var headerValue = headers[headerName];
32223 this.xhr.setRequestHeader(headerName, headerValue);
32229 this.xhr.onload = function()
32231 _this.xhrOnLoad(_this.xhr);
32234 this.xhr.onerror = function()
32236 _this.xhrOnError(_this.xhr);
32239 var formData = new FormData();
32241 formData.append('returnHTML', 'NO');
32243 formData.append('crop', crop);
32245 if(typeof(file.filename) != 'undefined'){
32246 formData.append('filename', file.filename);
32249 if(typeof(file.mimetype) != 'undefined'){
32250 formData.append('mimetype', file.mimetype);
32255 if(this.fireEvent('prepare', this, formData) != false){
32256 this.xhr.send(formData);
32266 * @class Roo.bootstrap.DocumentViewer
32267 * @extends Roo.bootstrap.Component
32268 * Bootstrap DocumentViewer class
32269 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32270 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32273 * Create a new DocumentViewer
32274 * @param {Object} config The config object
32277 Roo.bootstrap.DocumentViewer = function(config){
32278 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32283 * Fire after initEvent
32284 * @param {Roo.bootstrap.DocumentViewer} this
32290 * @param {Roo.bootstrap.DocumentViewer} this
32295 * Fire after download button
32296 * @param {Roo.bootstrap.DocumentViewer} this
32301 * Fire after trash button
32302 * @param {Roo.bootstrap.DocumentViewer} this
32309 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32311 showDownload : true,
32315 getAutoCreate : function()
32319 cls : 'roo-document-viewer',
32323 cls : 'roo-document-viewer-body',
32327 cls : 'roo-document-viewer-thumb',
32331 cls : 'roo-document-viewer-image'
32339 cls : 'roo-document-viewer-footer',
32342 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32346 cls : 'btn-group roo-document-viewer-download',
32350 cls : 'btn btn-default',
32351 html : '<i class="fa fa-download"></i>'
32357 cls : 'btn-group roo-document-viewer-trash',
32361 cls : 'btn btn-default',
32362 html : '<i class="fa fa-trash"></i>'
32375 initEvents : function()
32377 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32378 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32380 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32381 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32383 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32384 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32386 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32387 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32389 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32390 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32392 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32393 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32395 this.bodyEl.on('click', this.onClick, this);
32396 this.downloadBtn.on('click', this.onDownload, this);
32397 this.trashBtn.on('click', this.onTrash, this);
32399 this.downloadBtn.hide();
32400 this.trashBtn.hide();
32402 if(this.showDownload){
32403 this.downloadBtn.show();
32406 if(this.showTrash){
32407 this.trashBtn.show();
32410 if(!this.showDownload && !this.showTrash) {
32411 this.footerEl.hide();
32416 initial : function()
32418 this.fireEvent('initial', this);
32422 onClick : function(e)
32424 e.preventDefault();
32426 this.fireEvent('click', this);
32429 onDownload : function(e)
32431 e.preventDefault();
32433 this.fireEvent('download', this);
32436 onTrash : function(e)
32438 e.preventDefault();
32440 this.fireEvent('trash', this);
32452 * @class Roo.bootstrap.NavProgressBar
32453 * @extends Roo.bootstrap.Component
32454 * Bootstrap NavProgressBar class
32457 * Create a new nav progress bar
32458 * @param {Object} config The config object
32461 Roo.bootstrap.NavProgressBar = function(config){
32462 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32464 this.bullets = this.bullets || [];
32466 // Roo.bootstrap.NavProgressBar.register(this);
32470 * Fires when the active item changes
32471 * @param {Roo.bootstrap.NavProgressBar} this
32472 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32473 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32480 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32485 getAutoCreate : function()
32487 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32491 cls : 'roo-navigation-bar-group',
32495 cls : 'roo-navigation-top-bar'
32499 cls : 'roo-navigation-bullets-bar',
32503 cls : 'roo-navigation-bar'
32510 cls : 'roo-navigation-bottom-bar'
32520 initEvents: function()
32525 onRender : function(ct, position)
32527 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32529 if(this.bullets.length){
32530 Roo.each(this.bullets, function(b){
32539 addItem : function(cfg)
32541 var item = new Roo.bootstrap.NavProgressItem(cfg);
32543 item.parentId = this.id;
32544 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32547 var top = new Roo.bootstrap.Element({
32549 cls : 'roo-navigation-bar-text'
32552 var bottom = new Roo.bootstrap.Element({
32554 cls : 'roo-navigation-bar-text'
32557 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32558 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32560 var topText = new Roo.bootstrap.Element({
32562 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32565 var bottomText = new Roo.bootstrap.Element({
32567 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32570 topText.onRender(top.el, null);
32571 bottomText.onRender(bottom.el, null);
32574 item.bottomEl = bottom;
32577 this.barItems.push(item);
32582 getActive : function()
32584 var active = false;
32586 Roo.each(this.barItems, function(v){
32588 if (!v.isActive()) {
32600 setActiveItem : function(item)
32604 Roo.each(this.barItems, function(v){
32605 if (v.rid == item.rid) {
32609 if (v.isActive()) {
32610 v.setActive(false);
32615 item.setActive(true);
32617 this.fireEvent('changed', this, item, prev);
32620 getBarItem: function(rid)
32624 Roo.each(this.barItems, function(e) {
32625 if (e.rid != rid) {
32636 indexOfItem : function(item)
32640 Roo.each(this.barItems, function(v, i){
32642 if (v.rid != item.rid) {
32653 setActiveNext : function()
32655 var i = this.indexOfItem(this.getActive());
32657 if (i > this.barItems.length) {
32661 this.setActiveItem(this.barItems[i+1]);
32664 setActivePrev : function()
32666 var i = this.indexOfItem(this.getActive());
32672 this.setActiveItem(this.barItems[i-1]);
32675 format : function()
32677 if(!this.barItems.length){
32681 var width = 100 / this.barItems.length;
32683 Roo.each(this.barItems, function(i){
32684 i.el.setStyle('width', width + '%');
32685 i.topEl.el.setStyle('width', width + '%');
32686 i.bottomEl.el.setStyle('width', width + '%');
32695 * Nav Progress Item
32700 * @class Roo.bootstrap.NavProgressItem
32701 * @extends Roo.bootstrap.Component
32702 * Bootstrap NavProgressItem class
32703 * @cfg {String} rid the reference id
32704 * @cfg {Boolean} active (true|false) Is item active default false
32705 * @cfg {Boolean} disabled (true|false) Is item active default false
32706 * @cfg {String} html
32707 * @cfg {String} position (top|bottom) text position default bottom
32708 * @cfg {String} icon show icon instead of number
32711 * Create a new NavProgressItem
32712 * @param {Object} config The config object
32714 Roo.bootstrap.NavProgressItem = function(config){
32715 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32720 * The raw click event for the entire grid.
32721 * @param {Roo.bootstrap.NavProgressItem} this
32722 * @param {Roo.EventObject} e
32729 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
32735 position : 'bottom',
32738 getAutoCreate : function()
32740 var iconCls = 'roo-navigation-bar-item-icon';
32742 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32746 cls: 'roo-navigation-bar-item',
32756 cfg.cls += ' active';
32759 cfg.cls += ' disabled';
32765 disable : function()
32767 this.setDisabled(true);
32770 enable : function()
32772 this.setDisabled(false);
32775 initEvents: function()
32777 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32779 this.iconEl.on('click', this.onClick, this);
32782 onClick : function(e)
32784 e.preventDefault();
32790 if(this.fireEvent('click', this, e) === false){
32794 this.parent().setActiveItem(this);
32797 isActive: function ()
32799 return this.active;
32802 setActive : function(state)
32804 if(this.active == state){
32808 this.active = state;
32811 this.el.addClass('active');
32815 this.el.removeClass('active');
32820 setDisabled : function(state)
32822 if(this.disabled == state){
32826 this.disabled = state;
32829 this.el.addClass('disabled');
32833 this.el.removeClass('disabled');
32836 tooltipEl : function()
32838 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32851 * @class Roo.bootstrap.FieldLabel
32852 * @extends Roo.bootstrap.Component
32853 * Bootstrap FieldLabel class
32854 * @cfg {String} html contents of the element
32855 * @cfg {String} tag tag of the element default label
32856 * @cfg {String} cls class of the element
32857 * @cfg {String} target label target
32858 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32859 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32860 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32861 * @cfg {String} iconTooltip default "This field is required"
32862 * @cfg {String} indicatorpos (left|right) default left
32865 * Create a new FieldLabel
32866 * @param {Object} config The config object
32869 Roo.bootstrap.FieldLabel = function(config){
32870 Roo.bootstrap.Element.superclass.constructor.call(this, config);
32875 * Fires after the field has been marked as invalid.
32876 * @param {Roo.form.FieldLabel} this
32877 * @param {String} msg The validation message
32882 * Fires after the field has been validated with no errors.
32883 * @param {Roo.form.FieldLabel} this
32889 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
32896 invalidClass : 'has-warning',
32897 validClass : 'has-success',
32898 iconTooltip : 'This field is required',
32899 indicatorpos : 'left',
32901 getAutoCreate : function(){
32904 if (!this.allowBlank) {
32910 cls : 'roo-bootstrap-field-label ' + this.cls,
32915 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32916 tooltip : this.iconTooltip
32925 if(this.indicatorpos == 'right'){
32928 cls : 'roo-bootstrap-field-label ' + this.cls,
32937 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32938 tooltip : this.iconTooltip
32947 initEvents: function()
32949 Roo.bootstrap.Element.superclass.initEvents.call(this);
32951 this.indicator = this.indicatorEl();
32953 if(this.indicator){
32954 this.indicator.removeClass('visible');
32955 this.indicator.addClass('invisible');
32958 Roo.bootstrap.FieldLabel.register(this);
32961 indicatorEl : function()
32963 var indicator = this.el.select('i.roo-required-indicator',true).first();
32974 * Mark this field as valid
32976 markValid : function()
32978 if(this.indicator){
32979 this.indicator.removeClass('visible');
32980 this.indicator.addClass('invisible');
32982 if (Roo.bootstrap.version == 3) {
32983 this.el.removeClass(this.invalidClass);
32984 this.el.addClass(this.validClass);
32986 this.el.removeClass('is-invalid');
32987 this.el.addClass('is-valid');
32991 this.fireEvent('valid', this);
32995 * Mark this field as invalid
32996 * @param {String} msg The validation message
32998 markInvalid : function(msg)
33000 if(this.indicator){
33001 this.indicator.removeClass('invisible');
33002 this.indicator.addClass('visible');
33004 if (Roo.bootstrap.version == 3) {
33005 this.el.removeClass(this.validClass);
33006 this.el.addClass(this.invalidClass);
33008 this.el.removeClass('is-valid');
33009 this.el.addClass('is-invalid');
33013 this.fireEvent('invalid', this, msg);
33019 Roo.apply(Roo.bootstrap.FieldLabel, {
33024 * register a FieldLabel Group
33025 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33027 register : function(label)
33029 if(this.groups.hasOwnProperty(label.target)){
33033 this.groups[label.target] = label;
33037 * fetch a FieldLabel Group based on the target
33038 * @param {string} target
33039 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33041 get: function(target) {
33042 if (typeof(this.groups[target]) == 'undefined') {
33046 return this.groups[target] ;
33055 * page DateSplitField.
33061 * @class Roo.bootstrap.DateSplitField
33062 * @extends Roo.bootstrap.Component
33063 * Bootstrap DateSplitField class
33064 * @cfg {string} fieldLabel - the label associated
33065 * @cfg {Number} labelWidth set the width of label (0-12)
33066 * @cfg {String} labelAlign (top|left)
33067 * @cfg {Boolean} dayAllowBlank (true|false) default false
33068 * @cfg {Boolean} monthAllowBlank (true|false) default false
33069 * @cfg {Boolean} yearAllowBlank (true|false) default false
33070 * @cfg {string} dayPlaceholder
33071 * @cfg {string} monthPlaceholder
33072 * @cfg {string} yearPlaceholder
33073 * @cfg {string} dayFormat default 'd'
33074 * @cfg {string} monthFormat default 'm'
33075 * @cfg {string} yearFormat default 'Y'
33076 * @cfg {Number} labellg set the width of label (1-12)
33077 * @cfg {Number} labelmd set the width of label (1-12)
33078 * @cfg {Number} labelsm set the width of label (1-12)
33079 * @cfg {Number} labelxs set the width of label (1-12)
33083 * Create a new DateSplitField
33084 * @param {Object} config The config object
33087 Roo.bootstrap.DateSplitField = function(config){
33088 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33094 * getting the data of years
33095 * @param {Roo.bootstrap.DateSplitField} this
33096 * @param {Object} years
33101 * getting the data of days
33102 * @param {Roo.bootstrap.DateSplitField} this
33103 * @param {Object} days
33108 * Fires after the field has been marked as invalid.
33109 * @param {Roo.form.Field} this
33110 * @param {String} msg The validation message
33115 * Fires after the field has been validated with no errors.
33116 * @param {Roo.form.Field} this
33122 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33125 labelAlign : 'top',
33127 dayAllowBlank : false,
33128 monthAllowBlank : false,
33129 yearAllowBlank : false,
33130 dayPlaceholder : '',
33131 monthPlaceholder : '',
33132 yearPlaceholder : '',
33136 isFormField : true,
33142 getAutoCreate : function()
33146 cls : 'row roo-date-split-field-group',
33151 cls : 'form-hidden-field roo-date-split-field-group-value',
33157 var labelCls = 'col-md-12';
33158 var contentCls = 'col-md-4';
33160 if(this.fieldLabel){
33164 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33168 html : this.fieldLabel
33173 if(this.labelAlign == 'left'){
33175 if(this.labelWidth > 12){
33176 label.style = "width: " + this.labelWidth + 'px';
33179 if(this.labelWidth < 13 && this.labelmd == 0){
33180 this.labelmd = this.labelWidth;
33183 if(this.labellg > 0){
33184 labelCls = ' col-lg-' + this.labellg;
33185 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33188 if(this.labelmd > 0){
33189 labelCls = ' col-md-' + this.labelmd;
33190 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33193 if(this.labelsm > 0){
33194 labelCls = ' col-sm-' + this.labelsm;
33195 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33198 if(this.labelxs > 0){
33199 labelCls = ' col-xs-' + this.labelxs;
33200 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33204 label.cls += ' ' + labelCls;
33206 cfg.cn.push(label);
33209 Roo.each(['day', 'month', 'year'], function(t){
33212 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33219 inputEl: function ()
33221 return this.el.select('.roo-date-split-field-group-value', true).first();
33224 onRender : function(ct, position)
33228 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33230 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33232 this.dayField = new Roo.bootstrap.ComboBox({
33233 allowBlank : this.dayAllowBlank,
33234 alwaysQuery : true,
33235 displayField : 'value',
33238 forceSelection : true,
33240 placeholder : this.dayPlaceholder,
33241 selectOnFocus : true,
33242 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33243 triggerAction : 'all',
33245 valueField : 'value',
33246 store : new Roo.data.SimpleStore({
33247 data : (function() {
33249 _this.fireEvent('days', _this, days);
33252 fields : [ 'value' ]
33255 select : function (_self, record, index)
33257 _this.setValue(_this.getValue());
33262 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33264 this.monthField = new Roo.bootstrap.MonthField({
33265 after : '<i class=\"fa fa-calendar\"></i>',
33266 allowBlank : this.monthAllowBlank,
33267 placeholder : this.monthPlaceholder,
33270 render : function (_self)
33272 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33273 e.preventDefault();
33277 select : function (_self, oldvalue, newvalue)
33279 _this.setValue(_this.getValue());
33284 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33286 this.yearField = new Roo.bootstrap.ComboBox({
33287 allowBlank : this.yearAllowBlank,
33288 alwaysQuery : true,
33289 displayField : 'value',
33292 forceSelection : true,
33294 placeholder : this.yearPlaceholder,
33295 selectOnFocus : true,
33296 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33297 triggerAction : 'all',
33299 valueField : 'value',
33300 store : new Roo.data.SimpleStore({
33301 data : (function() {
33303 _this.fireEvent('years', _this, years);
33306 fields : [ 'value' ]
33309 select : function (_self, record, index)
33311 _this.setValue(_this.getValue());
33316 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33319 setValue : function(v, format)
33321 this.inputEl.dom.value = v;
33323 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33325 var d = Date.parseDate(v, f);
33332 this.setDay(d.format(this.dayFormat));
33333 this.setMonth(d.format(this.monthFormat));
33334 this.setYear(d.format(this.yearFormat));
33341 setDay : function(v)
33343 this.dayField.setValue(v);
33344 this.inputEl.dom.value = this.getValue();
33349 setMonth : function(v)
33351 this.monthField.setValue(v, true);
33352 this.inputEl.dom.value = this.getValue();
33357 setYear : function(v)
33359 this.yearField.setValue(v);
33360 this.inputEl.dom.value = this.getValue();
33365 getDay : function()
33367 return this.dayField.getValue();
33370 getMonth : function()
33372 return this.monthField.getValue();
33375 getYear : function()
33377 return this.yearField.getValue();
33380 getValue : function()
33382 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33384 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33394 this.inputEl.dom.value = '';
33399 validate : function()
33401 var d = this.dayField.validate();
33402 var m = this.monthField.validate();
33403 var y = this.yearField.validate();
33408 (!this.dayAllowBlank && !d) ||
33409 (!this.monthAllowBlank && !m) ||
33410 (!this.yearAllowBlank && !y)
33415 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33424 this.markInvalid();
33429 markValid : function()
33432 var label = this.el.select('label', true).first();
33433 var icon = this.el.select('i.fa-star', true).first();
33439 this.fireEvent('valid', this);
33443 * Mark this field as invalid
33444 * @param {String} msg The validation message
33446 markInvalid : function(msg)
33449 var label = this.el.select('label', true).first();
33450 var icon = this.el.select('i.fa-star', true).first();
33452 if(label && !icon){
33453 this.el.select('.roo-date-split-field-label', true).createChild({
33455 cls : 'text-danger fa fa-lg fa-star',
33456 tooltip : 'This field is required',
33457 style : 'margin-right:5px;'
33461 this.fireEvent('invalid', this, msg);
33464 clearInvalid : function()
33466 var label = this.el.select('label', true).first();
33467 var icon = this.el.select('i.fa-star', true).first();
33473 this.fireEvent('valid', this);
33476 getName: function()
33486 * http://masonry.desandro.com
33488 * The idea is to render all the bricks based on vertical width...
33490 * The original code extends 'outlayer' - we might need to use that....
33496 * @class Roo.bootstrap.LayoutMasonry
33497 * @extends Roo.bootstrap.Component
33498 * Bootstrap Layout Masonry class
33501 * Create a new Element
33502 * @param {Object} config The config object
33505 Roo.bootstrap.LayoutMasonry = function(config){
33507 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33511 Roo.bootstrap.LayoutMasonry.register(this);
33517 * Fire after layout the items
33518 * @param {Roo.bootstrap.LayoutMasonry} this
33519 * @param {Roo.EventObject} e
33526 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33529 * @cfg {Boolean} isLayoutInstant = no animation?
33531 isLayoutInstant : false, // needed?
33534 * @cfg {Number} boxWidth width of the columns
33539 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33544 * @cfg {Number} padWidth padding below box..
33549 * @cfg {Number} gutter gutter width..
33554 * @cfg {Number} maxCols maximum number of columns
33560 * @cfg {Boolean} isAutoInitial defalut true
33562 isAutoInitial : true,
33567 * @cfg {Boolean} isHorizontal defalut false
33569 isHorizontal : false,
33571 currentSize : null,
33577 bricks: null, //CompositeElement
33581 _isLayoutInited : false,
33583 // isAlternative : false, // only use for vertical layout...
33586 * @cfg {Number} alternativePadWidth padding below box..
33588 alternativePadWidth : 50,
33590 selectedBrick : [],
33592 getAutoCreate : function(){
33594 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33598 cls: 'blog-masonary-wrapper ' + this.cls,
33600 cls : 'mas-boxes masonary'
33607 getChildContainer: function( )
33609 if (this.boxesEl) {
33610 return this.boxesEl;
33613 this.boxesEl = this.el.select('.mas-boxes').first();
33615 return this.boxesEl;
33619 initEvents : function()
33623 if(this.isAutoInitial){
33624 Roo.log('hook children rendered');
33625 this.on('childrenrendered', function() {
33626 Roo.log('children rendered');
33632 initial : function()
33634 this.selectedBrick = [];
33636 this.currentSize = this.el.getBox(true);
33638 Roo.EventManager.onWindowResize(this.resize, this);
33640 if(!this.isAutoInitial){
33648 //this.layout.defer(500,this);
33652 resize : function()
33654 var cs = this.el.getBox(true);
33657 this.currentSize.width == cs.width &&
33658 this.currentSize.x == cs.x &&
33659 this.currentSize.height == cs.height &&
33660 this.currentSize.y == cs.y
33662 Roo.log("no change in with or X or Y");
33666 this.currentSize = cs;
33672 layout : function()
33674 this._resetLayout();
33676 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33678 this.layoutItems( isInstant );
33680 this._isLayoutInited = true;
33682 this.fireEvent('layout', this);
33686 _resetLayout : function()
33688 if(this.isHorizontal){
33689 this.horizontalMeasureColumns();
33693 this.verticalMeasureColumns();
33697 verticalMeasureColumns : function()
33699 this.getContainerWidth();
33701 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33702 // this.colWidth = Math.floor(this.containerWidth * 0.8);
33706 var boxWidth = this.boxWidth + this.padWidth;
33708 if(this.containerWidth < this.boxWidth){
33709 boxWidth = this.containerWidth
33712 var containerWidth = this.containerWidth;
33714 var cols = Math.floor(containerWidth / boxWidth);
33716 this.cols = Math.max( cols, 1 );
33718 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33720 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33722 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33724 this.colWidth = boxWidth + avail - this.padWidth;
33726 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33727 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
33730 horizontalMeasureColumns : function()
33732 this.getContainerWidth();
33734 var boxWidth = this.boxWidth;
33736 if(this.containerWidth < boxWidth){
33737 boxWidth = this.containerWidth;
33740 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33742 this.el.setHeight(boxWidth);
33746 getContainerWidth : function()
33748 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
33751 layoutItems : function( isInstant )
33753 Roo.log(this.bricks);
33755 var items = Roo.apply([], this.bricks);
33757 if(this.isHorizontal){
33758 this._horizontalLayoutItems( items , isInstant );
33762 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33763 // this._verticalAlternativeLayoutItems( items , isInstant );
33767 this._verticalLayoutItems( items , isInstant );
33771 _verticalLayoutItems : function ( items , isInstant)
33773 if ( !items || !items.length ) {
33778 ['xs', 'xs', 'xs', 'tall'],
33779 ['xs', 'xs', 'tall'],
33780 ['xs', 'xs', 'sm'],
33781 ['xs', 'xs', 'xs'],
33787 ['sm', 'xs', 'xs'],
33791 ['tall', 'xs', 'xs', 'xs'],
33792 ['tall', 'xs', 'xs'],
33804 Roo.each(items, function(item, k){
33806 switch (item.size) {
33807 // these layouts take up a full box,
33818 boxes.push([item]);
33841 var filterPattern = function(box, length)
33849 var pattern = box.slice(0, length);
33853 Roo.each(pattern, function(i){
33854 format.push(i.size);
33857 Roo.each(standard, function(s){
33859 if(String(s) != String(format)){
33868 if(!match && length == 1){
33873 filterPattern(box, length - 1);
33877 queue.push(pattern);
33879 box = box.slice(length, box.length);
33881 filterPattern(box, 4);
33887 Roo.each(boxes, function(box, k){
33893 if(box.length == 1){
33898 filterPattern(box, 4);
33902 this._processVerticalLayoutQueue( queue, isInstant );
33906 // _verticalAlternativeLayoutItems : function( items , isInstant )
33908 // if ( !items || !items.length ) {
33912 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
33916 _horizontalLayoutItems : function ( items , isInstant)
33918 if ( !items || !items.length || items.length < 3) {
33924 var eItems = items.slice(0, 3);
33926 items = items.slice(3, items.length);
33929 ['xs', 'xs', 'xs', 'wide'],
33930 ['xs', 'xs', 'wide'],
33931 ['xs', 'xs', 'sm'],
33932 ['xs', 'xs', 'xs'],
33938 ['sm', 'xs', 'xs'],
33942 ['wide', 'xs', 'xs', 'xs'],
33943 ['wide', 'xs', 'xs'],
33956 Roo.each(items, function(item, k){
33958 switch (item.size) {
33969 boxes.push([item]);
33993 var filterPattern = function(box, length)
34001 var pattern = box.slice(0, length);
34005 Roo.each(pattern, function(i){
34006 format.push(i.size);
34009 Roo.each(standard, function(s){
34011 if(String(s) != String(format)){
34020 if(!match && length == 1){
34025 filterPattern(box, length - 1);
34029 queue.push(pattern);
34031 box = box.slice(length, box.length);
34033 filterPattern(box, 4);
34039 Roo.each(boxes, function(box, k){
34045 if(box.length == 1){
34050 filterPattern(box, 4);
34057 var pos = this.el.getBox(true);
34061 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34063 var hit_end = false;
34065 Roo.each(queue, function(box){
34069 Roo.each(box, function(b){
34071 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34081 Roo.each(box, function(b){
34083 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34086 mx = Math.max(mx, b.x);
34090 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34094 Roo.each(box, function(b){
34096 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34110 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34113 /** Sets position of item in DOM
34114 * @param {Element} item
34115 * @param {Number} x - horizontal position
34116 * @param {Number} y - vertical position
34117 * @param {Boolean} isInstant - disables transitions
34119 _processVerticalLayoutQueue : function( queue, isInstant )
34121 var pos = this.el.getBox(true);
34126 for (var i = 0; i < this.cols; i++){
34130 Roo.each(queue, function(box, k){
34132 var col = k % this.cols;
34134 Roo.each(box, function(b,kk){
34136 b.el.position('absolute');
34138 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34139 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34141 if(b.size == 'md-left' || b.size == 'md-right'){
34142 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34143 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34146 b.el.setWidth(width);
34147 b.el.setHeight(height);
34149 b.el.select('iframe',true).setSize(width,height);
34153 for (var i = 0; i < this.cols; i++){
34155 if(maxY[i] < maxY[col]){
34160 col = Math.min(col, i);
34164 x = pos.x + col * (this.colWidth + this.padWidth);
34168 var positions = [];
34170 switch (box.length){
34172 positions = this.getVerticalOneBoxColPositions(x, y, box);
34175 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34178 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34181 positions = this.getVerticalFourBoxColPositions(x, y, box);
34187 Roo.each(box, function(b,kk){
34189 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34191 var sz = b.el.getSize();
34193 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34201 for (var i = 0; i < this.cols; i++){
34202 mY = Math.max(mY, maxY[i]);
34205 this.el.setHeight(mY - pos.y);
34209 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34211 // var pos = this.el.getBox(true);
34214 // var maxX = pos.right;
34216 // var maxHeight = 0;
34218 // Roo.each(items, function(item, k){
34222 // item.el.position('absolute');
34224 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34226 // item.el.setWidth(width);
34228 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34230 // item.el.setHeight(height);
34233 // item.el.setXY([x, y], isInstant ? false : true);
34235 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34238 // y = y + height + this.alternativePadWidth;
34240 // maxHeight = maxHeight + height + this.alternativePadWidth;
34244 // this.el.setHeight(maxHeight);
34248 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34250 var pos = this.el.getBox(true);
34255 var maxX = pos.right;
34257 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34259 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34261 Roo.each(queue, function(box, k){
34263 Roo.each(box, function(b, kk){
34265 b.el.position('absolute');
34267 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34268 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34270 if(b.size == 'md-left' || b.size == 'md-right'){
34271 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34272 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34275 b.el.setWidth(width);
34276 b.el.setHeight(height);
34284 var positions = [];
34286 switch (box.length){
34288 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34291 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34294 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34297 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34303 Roo.each(box, function(b,kk){
34305 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34307 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34315 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34317 Roo.each(eItems, function(b,k){
34319 b.size = (k == 0) ? 'sm' : 'xs';
34320 b.x = (k == 0) ? 2 : 1;
34321 b.y = (k == 0) ? 2 : 1;
34323 b.el.position('absolute');
34325 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34327 b.el.setWidth(width);
34329 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34331 b.el.setHeight(height);
34335 var positions = [];
34338 x : maxX - this.unitWidth * 2 - this.gutter,
34343 x : maxX - this.unitWidth,
34344 y : minY + (this.unitWidth + this.gutter) * 2
34348 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34352 Roo.each(eItems, function(b,k){
34354 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34360 getVerticalOneBoxColPositions : function(x, y, box)
34364 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34366 if(box[0].size == 'md-left'){
34370 if(box[0].size == 'md-right'){
34375 x : x + (this.unitWidth + this.gutter) * rand,
34382 getVerticalTwoBoxColPositions : function(x, y, box)
34386 if(box[0].size == 'xs'){
34390 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34394 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34408 x : x + (this.unitWidth + this.gutter) * 2,
34409 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34416 getVerticalThreeBoxColPositions : function(x, y, box)
34420 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34428 x : x + (this.unitWidth + this.gutter) * 1,
34433 x : x + (this.unitWidth + this.gutter) * 2,
34441 if(box[0].size == 'xs' && box[1].size == 'xs'){
34450 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34454 x : x + (this.unitWidth + this.gutter) * 1,
34468 x : x + (this.unitWidth + this.gutter) * 2,
34473 x : x + (this.unitWidth + this.gutter) * 2,
34474 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34481 getVerticalFourBoxColPositions : function(x, y, box)
34485 if(box[0].size == 'xs'){
34494 y : y + (this.unitHeight + this.gutter) * 1
34499 y : y + (this.unitHeight + this.gutter) * 2
34503 x : x + (this.unitWidth + this.gutter) * 1,
34517 x : x + (this.unitWidth + this.gutter) * 2,
34522 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34523 y : y + (this.unitHeight + this.gutter) * 1
34527 x : x + (this.unitWidth + this.gutter) * 2,
34528 y : y + (this.unitWidth + this.gutter) * 2
34535 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34539 if(box[0].size == 'md-left'){
34541 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34548 if(box[0].size == 'md-right'){
34550 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34551 y : minY + (this.unitWidth + this.gutter) * 1
34557 var rand = Math.floor(Math.random() * (4 - box[0].y));
34560 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34561 y : minY + (this.unitWidth + this.gutter) * rand
34568 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34572 if(box[0].size == 'xs'){
34575 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34580 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34581 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34589 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34594 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34595 y : minY + (this.unitWidth + this.gutter) * 2
34602 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34606 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34609 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34614 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34615 y : minY + (this.unitWidth + this.gutter) * 1
34619 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34620 y : minY + (this.unitWidth + this.gutter) * 2
34627 if(box[0].size == 'xs' && box[1].size == 'xs'){
34630 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34635 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34640 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34641 y : minY + (this.unitWidth + this.gutter) * 1
34649 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34654 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34655 y : minY + (this.unitWidth + this.gutter) * 2
34659 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34660 y : minY + (this.unitWidth + this.gutter) * 2
34667 getHorizontalFourBoxColPositions : function(maxX, minY, box)
34671 if(box[0].size == 'xs'){
34674 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34679 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34684 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),
34689 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34690 y : minY + (this.unitWidth + this.gutter) * 1
34698 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34703 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34704 y : minY + (this.unitWidth + this.gutter) * 2
34708 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34709 y : minY + (this.unitWidth + this.gutter) * 2
34713 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),
34714 y : minY + (this.unitWidth + this.gutter) * 2
34722 * remove a Masonry Brick
34723 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34725 removeBrick : function(brick_id)
34731 for (var i = 0; i<this.bricks.length; i++) {
34732 if (this.bricks[i].id == brick_id) {
34733 this.bricks.splice(i,1);
34734 this.el.dom.removeChild(Roo.get(brick_id).dom);
34741 * adds a Masonry Brick
34742 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34744 addBrick : function(cfg)
34746 var cn = new Roo.bootstrap.MasonryBrick(cfg);
34747 //this.register(cn);
34748 cn.parentId = this.id;
34749 cn.render(this.el);
34754 * register a Masonry Brick
34755 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34758 register : function(brick)
34760 this.bricks.push(brick);
34761 brick.masonryId = this.id;
34765 * clear all the Masonry Brick
34767 clearAll : function()
34770 //this.getChildContainer().dom.innerHTML = "";
34771 this.el.dom.innerHTML = '';
34774 getSelected : function()
34776 if (!this.selectedBrick) {
34780 return this.selectedBrick;
34784 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34788 * register a Masonry Layout
34789 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34792 register : function(layout)
34794 this.groups[layout.id] = layout;
34797 * fetch a Masonry Layout based on the masonry layout ID
34798 * @param {string} the masonry layout to add
34799 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34802 get: function(layout_id) {
34803 if (typeof(this.groups[layout_id]) == 'undefined') {
34806 return this.groups[layout_id] ;
34818 * http://masonry.desandro.com
34820 * The idea is to render all the bricks based on vertical width...
34822 * The original code extends 'outlayer' - we might need to use that....
34828 * @class Roo.bootstrap.LayoutMasonryAuto
34829 * @extends Roo.bootstrap.Component
34830 * Bootstrap Layout Masonry class
34833 * Create a new Element
34834 * @param {Object} config The config object
34837 Roo.bootstrap.LayoutMasonryAuto = function(config){
34838 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34841 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
34844 * @cfg {Boolean} isFitWidth - resize the width..
34846 isFitWidth : false, // options..
34848 * @cfg {Boolean} isOriginLeft = left align?
34850 isOriginLeft : true,
34852 * @cfg {Boolean} isOriginTop = top align?
34854 isOriginTop : false,
34856 * @cfg {Boolean} isLayoutInstant = no animation?
34858 isLayoutInstant : false, // needed?
34860 * @cfg {Boolean} isResizingContainer = not sure if this is used..
34862 isResizingContainer : true,
34864 * @cfg {Number} columnWidth width of the columns
34870 * @cfg {Number} maxCols maximum number of columns
34875 * @cfg {Number} padHeight padding below box..
34881 * @cfg {Boolean} isAutoInitial defalut true
34884 isAutoInitial : true,
34890 initialColumnWidth : 0,
34891 currentSize : null,
34893 colYs : null, // array.
34900 bricks: null, //CompositeElement
34901 cols : 0, // array?
34902 // element : null, // wrapped now this.el
34903 _isLayoutInited : null,
34906 getAutoCreate : function(){
34910 cls: 'blog-masonary-wrapper ' + this.cls,
34912 cls : 'mas-boxes masonary'
34919 getChildContainer: function( )
34921 if (this.boxesEl) {
34922 return this.boxesEl;
34925 this.boxesEl = this.el.select('.mas-boxes').first();
34927 return this.boxesEl;
34931 initEvents : function()
34935 if(this.isAutoInitial){
34936 Roo.log('hook children rendered');
34937 this.on('childrenrendered', function() {
34938 Roo.log('children rendered');
34945 initial : function()
34947 this.reloadItems();
34949 this.currentSize = this.el.getBox(true);
34951 /// was window resize... - let's see if this works..
34952 Roo.EventManager.onWindowResize(this.resize, this);
34954 if(!this.isAutoInitial){
34959 this.layout.defer(500,this);
34962 reloadItems: function()
34964 this.bricks = this.el.select('.masonry-brick', true);
34966 this.bricks.each(function(b) {
34967 //Roo.log(b.getSize());
34968 if (!b.attr('originalwidth')) {
34969 b.attr('originalwidth', b.getSize().width);
34974 Roo.log(this.bricks.elements.length);
34977 resize : function()
34980 var cs = this.el.getBox(true);
34982 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34983 Roo.log("no change in with or X");
34986 this.currentSize = cs;
34990 layout : function()
34993 this._resetLayout();
34994 //this._manageStamps();
34996 // don't animate first layout
34997 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34998 this.layoutItems( isInstant );
35000 // flag for initalized
35001 this._isLayoutInited = true;
35004 layoutItems : function( isInstant )
35006 //var items = this._getItemsForLayout( this.items );
35007 // original code supports filtering layout items.. we just ignore it..
35009 this._layoutItems( this.bricks , isInstant );
35011 this._postLayout();
35013 _layoutItems : function ( items , isInstant)
35015 //this.fireEvent( 'layout', this, items );
35018 if ( !items || !items.elements.length ) {
35019 // no items, emit event with empty array
35024 items.each(function(item) {
35025 Roo.log("layout item");
35027 // get x/y object from method
35028 var position = this._getItemLayoutPosition( item );
35030 position.item = item;
35031 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35032 queue.push( position );
35035 this._processLayoutQueue( queue );
35037 /** Sets position of item in DOM
35038 * @param {Element} item
35039 * @param {Number} x - horizontal position
35040 * @param {Number} y - vertical position
35041 * @param {Boolean} isInstant - disables transitions
35043 _processLayoutQueue : function( queue )
35045 for ( var i=0, len = queue.length; i < len; i++ ) {
35046 var obj = queue[i];
35047 obj.item.position('absolute');
35048 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35054 * Any logic you want to do after each layout,
35055 * i.e. size the container
35057 _postLayout : function()
35059 this.resizeContainer();
35062 resizeContainer : function()
35064 if ( !this.isResizingContainer ) {
35067 var size = this._getContainerSize();
35069 this.el.setSize(size.width,size.height);
35070 this.boxesEl.setSize(size.width,size.height);
35076 _resetLayout : function()
35078 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35079 this.colWidth = this.el.getWidth();
35080 //this.gutter = this.el.getWidth();
35082 this.measureColumns();
35088 this.colYs.push( 0 );
35094 measureColumns : function()
35096 this.getContainerWidth();
35097 // if columnWidth is 0, default to outerWidth of first item
35098 if ( !this.columnWidth ) {
35099 var firstItem = this.bricks.first();
35100 Roo.log(firstItem);
35101 this.columnWidth = this.containerWidth;
35102 if (firstItem && firstItem.attr('originalwidth') ) {
35103 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35105 // columnWidth fall back to item of first element
35106 Roo.log("set column width?");
35107 this.initialColumnWidth = this.columnWidth ;
35109 // if first elem has no width, default to size of container
35114 if (this.initialColumnWidth) {
35115 this.columnWidth = this.initialColumnWidth;
35120 // column width is fixed at the top - however if container width get's smaller we should
35123 // this bit calcs how man columns..
35125 var columnWidth = this.columnWidth += this.gutter;
35127 // calculate columns
35128 var containerWidth = this.containerWidth + this.gutter;
35130 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35131 // fix rounding errors, typically with gutters
35132 var excess = columnWidth - containerWidth % columnWidth;
35135 // if overshoot is less than a pixel, round up, otherwise floor it
35136 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35137 cols = Math[ mathMethod ]( cols );
35138 this.cols = Math.max( cols, 1 );
35139 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35141 // padding positioning..
35142 var totalColWidth = this.cols * this.columnWidth;
35143 var padavail = this.containerWidth - totalColWidth;
35144 // so for 2 columns - we need 3 'pads'
35146 var padNeeded = (1+this.cols) * this.padWidth;
35148 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35150 this.columnWidth += padExtra
35151 //this.padWidth = Math.floor(padavail / ( this.cols));
35153 // adjust colum width so that padding is fixed??
35155 // we have 3 columns ... total = width * 3
35156 // we have X left over... that should be used by
35158 //if (this.expandC) {
35166 getContainerWidth : function()
35168 /* // container is parent if fit width
35169 var container = this.isFitWidth ? this.element.parentNode : this.element;
35170 // check that this.size and size are there
35171 // IE8 triggers resize on body size change, so they might not be
35173 var size = getSize( container ); //FIXME
35174 this.containerWidth = size && size.innerWidth; //FIXME
35177 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35181 _getItemLayoutPosition : function( item ) // what is item?
35183 // we resize the item to our columnWidth..
35185 item.setWidth(this.columnWidth);
35186 item.autoBoxAdjust = false;
35188 var sz = item.getSize();
35190 // how many columns does this brick span
35191 var remainder = this.containerWidth % this.columnWidth;
35193 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35194 // round if off by 1 pixel, otherwise use ceil
35195 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35196 colSpan = Math.min( colSpan, this.cols );
35198 // normally this should be '1' as we dont' currently allow multi width columns..
35200 var colGroup = this._getColGroup( colSpan );
35201 // get the minimum Y value from the columns
35202 var minimumY = Math.min.apply( Math, colGroup );
35203 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35205 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35207 // position the brick
35209 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35210 y: this.currentSize.y + minimumY + this.padHeight
35214 // apply setHeight to necessary columns
35215 var setHeight = minimumY + sz.height + this.padHeight;
35216 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35218 var setSpan = this.cols + 1 - colGroup.length;
35219 for ( var i = 0; i < setSpan; i++ ) {
35220 this.colYs[ shortColIndex + i ] = setHeight ;
35227 * @param {Number} colSpan - number of columns the element spans
35228 * @returns {Array} colGroup
35230 _getColGroup : function( colSpan )
35232 if ( colSpan < 2 ) {
35233 // if brick spans only one column, use all the column Ys
35238 // how many different places could this brick fit horizontally
35239 var groupCount = this.cols + 1 - colSpan;
35240 // for each group potential horizontal position
35241 for ( var i = 0; i < groupCount; i++ ) {
35242 // make an array of colY values for that one group
35243 var groupColYs = this.colYs.slice( i, i + colSpan );
35244 // and get the max value of the array
35245 colGroup[i] = Math.max.apply( Math, groupColYs );
35250 _manageStamp : function( stamp )
35252 var stampSize = stamp.getSize();
35253 var offset = stamp.getBox();
35254 // get the columns that this stamp affects
35255 var firstX = this.isOriginLeft ? offset.x : offset.right;
35256 var lastX = firstX + stampSize.width;
35257 var firstCol = Math.floor( firstX / this.columnWidth );
35258 firstCol = Math.max( 0, firstCol );
35260 var lastCol = Math.floor( lastX / this.columnWidth );
35261 // lastCol should not go over if multiple of columnWidth #425
35262 lastCol -= lastX % this.columnWidth ? 0 : 1;
35263 lastCol = Math.min( this.cols - 1, lastCol );
35265 // set colYs to bottom of the stamp
35266 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35269 for ( var i = firstCol; i <= lastCol; i++ ) {
35270 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35275 _getContainerSize : function()
35277 this.maxY = Math.max.apply( Math, this.colYs );
35282 if ( this.isFitWidth ) {
35283 size.width = this._getContainerFitWidth();
35289 _getContainerFitWidth : function()
35291 var unusedCols = 0;
35292 // count unused columns
35295 if ( this.colYs[i] !== 0 ) {
35300 // fit container to columns that have been used
35301 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35304 needsResizeLayout : function()
35306 var previousWidth = this.containerWidth;
35307 this.getContainerWidth();
35308 return previousWidth !== this.containerWidth;
35323 * @class Roo.bootstrap.MasonryBrick
35324 * @extends Roo.bootstrap.Component
35325 * Bootstrap MasonryBrick class
35328 * Create a new MasonryBrick
35329 * @param {Object} config The config object
35332 Roo.bootstrap.MasonryBrick = function(config){
35334 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35336 Roo.bootstrap.MasonryBrick.register(this);
35342 * When a MasonryBrick is clcik
35343 * @param {Roo.bootstrap.MasonryBrick} this
35344 * @param {Roo.EventObject} e
35350 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35353 * @cfg {String} title
35357 * @cfg {String} html
35361 * @cfg {String} bgimage
35365 * @cfg {String} videourl
35369 * @cfg {String} cls
35373 * @cfg {String} href
35377 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35382 * @cfg {String} placetitle (center|bottom)
35387 * @cfg {Boolean} isFitContainer defalut true
35389 isFitContainer : true,
35392 * @cfg {Boolean} preventDefault defalut false
35394 preventDefault : false,
35397 * @cfg {Boolean} inverse defalut false
35399 maskInverse : false,
35401 getAutoCreate : function()
35403 if(!this.isFitContainer){
35404 return this.getSplitAutoCreate();
35407 var cls = 'masonry-brick masonry-brick-full';
35409 if(this.href.length){
35410 cls += ' masonry-brick-link';
35413 if(this.bgimage.length){
35414 cls += ' masonry-brick-image';
35417 if(this.maskInverse){
35418 cls += ' mask-inverse';
35421 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35422 cls += ' enable-mask';
35426 cls += ' masonry-' + this.size + '-brick';
35429 if(this.placetitle.length){
35431 switch (this.placetitle) {
35433 cls += ' masonry-center-title';
35436 cls += ' masonry-bottom-title';
35443 if(!this.html.length && !this.bgimage.length){
35444 cls += ' masonry-center-title';
35447 if(!this.html.length && this.bgimage.length){
35448 cls += ' masonry-bottom-title';
35453 cls += ' ' + this.cls;
35457 tag: (this.href.length) ? 'a' : 'div',
35462 cls: 'masonry-brick-mask'
35466 cls: 'masonry-brick-paragraph',
35472 if(this.href.length){
35473 cfg.href = this.href;
35476 var cn = cfg.cn[1].cn;
35478 if(this.title.length){
35481 cls: 'masonry-brick-title',
35486 if(this.html.length){
35489 cls: 'masonry-brick-text',
35494 if (!this.title.length && !this.html.length) {
35495 cfg.cn[1].cls += ' hide';
35498 if(this.bgimage.length){
35501 cls: 'masonry-brick-image-view',
35506 if(this.videourl.length){
35507 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35508 // youtube support only?
35511 cls: 'masonry-brick-image-view',
35514 allowfullscreen : true
35522 getSplitAutoCreate : function()
35524 var cls = 'masonry-brick masonry-brick-split';
35526 if(this.href.length){
35527 cls += ' masonry-brick-link';
35530 if(this.bgimage.length){
35531 cls += ' masonry-brick-image';
35535 cls += ' masonry-' + this.size + '-brick';
35538 switch (this.placetitle) {
35540 cls += ' masonry-center-title';
35543 cls += ' masonry-bottom-title';
35546 if(!this.bgimage.length){
35547 cls += ' masonry-center-title';
35550 if(this.bgimage.length){
35551 cls += ' masonry-bottom-title';
35557 cls += ' ' + this.cls;
35561 tag: (this.href.length) ? 'a' : 'div',
35566 cls: 'masonry-brick-split-head',
35570 cls: 'masonry-brick-paragraph',
35577 cls: 'masonry-brick-split-body',
35583 if(this.href.length){
35584 cfg.href = this.href;
35587 if(this.title.length){
35588 cfg.cn[0].cn[0].cn.push({
35590 cls: 'masonry-brick-title',
35595 if(this.html.length){
35596 cfg.cn[1].cn.push({
35598 cls: 'masonry-brick-text',
35603 if(this.bgimage.length){
35604 cfg.cn[0].cn.push({
35606 cls: 'masonry-brick-image-view',
35611 if(this.videourl.length){
35612 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35613 // youtube support only?
35614 cfg.cn[0].cn.cn.push({
35616 cls: 'masonry-brick-image-view',
35619 allowfullscreen : true
35626 initEvents: function()
35628 switch (this.size) {
35661 this.el.on('touchstart', this.onTouchStart, this);
35662 this.el.on('touchmove', this.onTouchMove, this);
35663 this.el.on('touchend', this.onTouchEnd, this);
35664 this.el.on('contextmenu', this.onContextMenu, this);
35666 this.el.on('mouseenter' ,this.enter, this);
35667 this.el.on('mouseleave', this.leave, this);
35668 this.el.on('click', this.onClick, this);
35671 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35672 this.parent().bricks.push(this);
35677 onClick: function(e, el)
35679 var time = this.endTimer - this.startTimer;
35680 // Roo.log(e.preventDefault());
35683 e.preventDefault();
35688 if(!this.preventDefault){
35692 e.preventDefault();
35694 if (this.activeClass != '') {
35695 this.selectBrick();
35698 this.fireEvent('click', this, e);
35701 enter: function(e, el)
35703 e.preventDefault();
35705 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35709 if(this.bgimage.length && this.html.length){
35710 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35714 leave: function(e, el)
35716 e.preventDefault();
35718 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35722 if(this.bgimage.length && this.html.length){
35723 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35727 onTouchStart: function(e, el)
35729 // e.preventDefault();
35731 this.touchmoved = false;
35733 if(!this.isFitContainer){
35737 if(!this.bgimage.length || !this.html.length){
35741 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35743 this.timer = new Date().getTime();
35747 onTouchMove: function(e, el)
35749 this.touchmoved = true;
35752 onContextMenu : function(e,el)
35754 e.preventDefault();
35755 e.stopPropagation();
35759 onTouchEnd: function(e, el)
35761 // e.preventDefault();
35763 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35770 if(!this.bgimage.length || !this.html.length){
35772 if(this.href.length){
35773 window.location.href = this.href;
35779 if(!this.isFitContainer){
35783 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35785 window.location.href = this.href;
35788 //selection on single brick only
35789 selectBrick : function() {
35791 if (!this.parentId) {
35795 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35796 var index = m.selectedBrick.indexOf(this.id);
35799 m.selectedBrick.splice(index,1);
35800 this.el.removeClass(this.activeClass);
35804 for(var i = 0; i < m.selectedBrick.length; i++) {
35805 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35806 b.el.removeClass(b.activeClass);
35809 m.selectedBrick = [];
35811 m.selectedBrick.push(this.id);
35812 this.el.addClass(this.activeClass);
35816 isSelected : function(){
35817 return this.el.hasClass(this.activeClass);
35822 Roo.apply(Roo.bootstrap.MasonryBrick, {
35825 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35827 * register a Masonry Brick
35828 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35831 register : function(brick)
35833 //this.groups[brick.id] = brick;
35834 this.groups.add(brick.id, brick);
35837 * fetch a masonry brick based on the masonry brick ID
35838 * @param {string} the masonry brick to add
35839 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35842 get: function(brick_id)
35844 // if (typeof(this.groups[brick_id]) == 'undefined') {
35847 // return this.groups[brick_id] ;
35849 if(this.groups.key(brick_id)) {
35850 return this.groups.key(brick_id);
35868 * @class Roo.bootstrap.Brick
35869 * @extends Roo.bootstrap.Component
35870 * Bootstrap Brick class
35873 * Create a new Brick
35874 * @param {Object} config The config object
35877 Roo.bootstrap.Brick = function(config){
35878 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35884 * When a Brick is click
35885 * @param {Roo.bootstrap.Brick} this
35886 * @param {Roo.EventObject} e
35892 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
35895 * @cfg {String} title
35899 * @cfg {String} html
35903 * @cfg {String} bgimage
35907 * @cfg {String} cls
35911 * @cfg {String} href
35915 * @cfg {String} video
35919 * @cfg {Boolean} square
35923 getAutoCreate : function()
35925 var cls = 'roo-brick';
35927 if(this.href.length){
35928 cls += ' roo-brick-link';
35931 if(this.bgimage.length){
35932 cls += ' roo-brick-image';
35935 if(!this.html.length && !this.bgimage.length){
35936 cls += ' roo-brick-center-title';
35939 if(!this.html.length && this.bgimage.length){
35940 cls += ' roo-brick-bottom-title';
35944 cls += ' ' + this.cls;
35948 tag: (this.href.length) ? 'a' : 'div',
35953 cls: 'roo-brick-paragraph',
35959 if(this.href.length){
35960 cfg.href = this.href;
35963 var cn = cfg.cn[0].cn;
35965 if(this.title.length){
35968 cls: 'roo-brick-title',
35973 if(this.html.length){
35976 cls: 'roo-brick-text',
35983 if(this.bgimage.length){
35986 cls: 'roo-brick-image-view',
35994 initEvents: function()
35996 if(this.title.length || this.html.length){
35997 this.el.on('mouseenter' ,this.enter, this);
35998 this.el.on('mouseleave', this.leave, this);
36001 Roo.EventManager.onWindowResize(this.resize, this);
36003 if(this.bgimage.length){
36004 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36005 this.imageEl.on('load', this.onImageLoad, this);
36012 onImageLoad : function()
36017 resize : function()
36019 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36021 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36023 if(this.bgimage.length){
36024 var image = this.el.select('.roo-brick-image-view', true).first();
36026 image.setWidth(paragraph.getWidth());
36029 image.setHeight(paragraph.getWidth());
36032 this.el.setHeight(image.getHeight());
36033 paragraph.setHeight(image.getHeight());
36039 enter: function(e, el)
36041 e.preventDefault();
36043 if(this.bgimage.length){
36044 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36045 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36049 leave: function(e, el)
36051 e.preventDefault();
36053 if(this.bgimage.length){
36054 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36055 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36070 * @class Roo.bootstrap.NumberField
36071 * @extends Roo.bootstrap.Input
36072 * Bootstrap NumberField class
36078 * Create a new NumberField
36079 * @param {Object} config The config object
36082 Roo.bootstrap.NumberField = function(config){
36083 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36086 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36089 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36091 allowDecimals : true,
36093 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36095 decimalSeparator : ".",
36097 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36099 decimalPrecision : 2,
36101 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36103 allowNegative : true,
36106 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36110 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36112 minValue : Number.NEGATIVE_INFINITY,
36114 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36116 maxValue : Number.MAX_VALUE,
36118 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36120 minText : "The minimum value for this field is {0}",
36122 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36124 maxText : "The maximum value for this field is {0}",
36126 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36127 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36129 nanText : "{0} is not a valid number",
36131 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36133 thousandsDelimiter : false,
36135 * @cfg {String} valueAlign alignment of value
36137 valueAlign : "left",
36139 getAutoCreate : function()
36141 var hiddenInput = {
36145 cls: 'hidden-number-input'
36149 hiddenInput.name = this.name;
36154 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36156 this.name = hiddenInput.name;
36158 if(cfg.cn.length > 0) {
36159 cfg.cn.push(hiddenInput);
36166 initEvents : function()
36168 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36170 var allowed = "0123456789";
36172 if(this.allowDecimals){
36173 allowed += this.decimalSeparator;
36176 if(this.allowNegative){
36180 if(this.thousandsDelimiter) {
36184 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36186 var keyPress = function(e){
36188 var k = e.getKey();
36190 var c = e.getCharCode();
36193 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36194 allowed.indexOf(String.fromCharCode(c)) === -1
36200 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36204 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36209 this.el.on("keypress", keyPress, this);
36212 validateValue : function(value)
36215 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36219 var num = this.parseValue(value);
36222 this.markInvalid(String.format(this.nanText, value));
36226 if(num < this.minValue){
36227 this.markInvalid(String.format(this.minText, this.minValue));
36231 if(num > this.maxValue){
36232 this.markInvalid(String.format(this.maxText, this.maxValue));
36239 getValue : function()
36241 var v = this.hiddenEl().getValue();
36243 return this.fixPrecision(this.parseValue(v));
36246 parseValue : function(value)
36248 if(this.thousandsDelimiter) {
36250 r = new RegExp(",", "g");
36251 value = value.replace(r, "");
36254 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36255 return isNaN(value) ? '' : value;
36258 fixPrecision : function(value)
36260 if(this.thousandsDelimiter) {
36262 r = new RegExp(",", "g");
36263 value = value.replace(r, "");
36266 var nan = isNaN(value);
36268 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36269 return nan ? '' : value;
36271 return parseFloat(value).toFixed(this.decimalPrecision);
36274 setValue : function(v)
36276 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36282 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36284 this.inputEl().dom.value = (v == '') ? '' :
36285 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36287 if(!this.allowZero && v === '0') {
36288 this.hiddenEl().dom.value = '';
36289 this.inputEl().dom.value = '';
36296 decimalPrecisionFcn : function(v)
36298 return Math.floor(v);
36301 beforeBlur : function()
36303 var v = this.parseValue(this.getRawValue());
36305 if(v || v === 0 || v === ''){
36310 hiddenEl : function()
36312 return this.el.select('input.hidden-number-input',true).first();
36324 * @class Roo.bootstrap.DocumentSlider
36325 * @extends Roo.bootstrap.Component
36326 * Bootstrap DocumentSlider class
36329 * Create a new DocumentViewer
36330 * @param {Object} config The config object
36333 Roo.bootstrap.DocumentSlider = function(config){
36334 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36341 * Fire after initEvent
36342 * @param {Roo.bootstrap.DocumentSlider} this
36347 * Fire after update
36348 * @param {Roo.bootstrap.DocumentSlider} this
36354 * @param {Roo.bootstrap.DocumentSlider} this
36360 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36366 getAutoCreate : function()
36370 cls : 'roo-document-slider',
36374 cls : 'roo-document-slider-header',
36378 cls : 'roo-document-slider-header-title'
36384 cls : 'roo-document-slider-body',
36388 cls : 'roo-document-slider-prev',
36392 cls : 'fa fa-chevron-left'
36398 cls : 'roo-document-slider-thumb',
36402 cls : 'roo-document-slider-image'
36408 cls : 'roo-document-slider-next',
36412 cls : 'fa fa-chevron-right'
36424 initEvents : function()
36426 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36427 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36429 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36430 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36432 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36433 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36435 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36436 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36438 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36439 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36441 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36442 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36444 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36445 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36447 this.thumbEl.on('click', this.onClick, this);
36449 this.prevIndicator.on('click', this.prev, this);
36451 this.nextIndicator.on('click', this.next, this);
36455 initial : function()
36457 if(this.files.length){
36458 this.indicator = 1;
36462 this.fireEvent('initial', this);
36465 update : function()
36467 this.imageEl.attr('src', this.files[this.indicator - 1]);
36469 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36471 this.prevIndicator.show();
36473 if(this.indicator == 1){
36474 this.prevIndicator.hide();
36477 this.nextIndicator.show();
36479 if(this.indicator == this.files.length){
36480 this.nextIndicator.hide();
36483 this.thumbEl.scrollTo('top');
36485 this.fireEvent('update', this);
36488 onClick : function(e)
36490 e.preventDefault();
36492 this.fireEvent('click', this);
36497 e.preventDefault();
36499 this.indicator = Math.max(1, this.indicator - 1);
36506 e.preventDefault();
36508 this.indicator = Math.min(this.files.length, this.indicator + 1);
36522 * @class Roo.bootstrap.RadioSet
36523 * @extends Roo.bootstrap.Input
36524 * Bootstrap RadioSet class
36525 * @cfg {String} indicatorpos (left|right) default left
36526 * @cfg {Boolean} inline (true|false) inline the element (default true)
36527 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36529 * Create a new RadioSet
36530 * @param {Object} config The config object
36533 Roo.bootstrap.RadioSet = function(config){
36535 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36539 Roo.bootstrap.RadioSet.register(this);
36544 * Fires when the element is checked or unchecked.
36545 * @param {Roo.bootstrap.RadioSet} this This radio
36546 * @param {Roo.bootstrap.Radio} item The checked item
36551 * Fires when the element is click.
36552 * @param {Roo.bootstrap.RadioSet} this This radio set
36553 * @param {Roo.bootstrap.Radio} item The checked item
36554 * @param {Roo.EventObject} e The event object
36561 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36569 indicatorpos : 'left',
36571 getAutoCreate : function()
36575 cls : 'roo-radio-set-label',
36579 html : this.fieldLabel
36583 if (Roo.bootstrap.version == 3) {
36586 if(this.indicatorpos == 'left'){
36589 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36590 tooltip : 'This field is required'
36595 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36596 tooltip : 'This field is required'
36602 cls : 'roo-radio-set-items'
36605 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36607 if (align === 'left' && this.fieldLabel.length) {
36610 cls : "roo-radio-set-right",
36616 if(this.labelWidth > 12){
36617 label.style = "width: " + this.labelWidth + 'px';
36620 if(this.labelWidth < 13 && this.labelmd == 0){
36621 this.labelmd = this.labelWidth;
36624 if(this.labellg > 0){
36625 label.cls += ' col-lg-' + this.labellg;
36626 items.cls += ' col-lg-' + (12 - this.labellg);
36629 if(this.labelmd > 0){
36630 label.cls += ' col-md-' + this.labelmd;
36631 items.cls += ' col-md-' + (12 - this.labelmd);
36634 if(this.labelsm > 0){
36635 label.cls += ' col-sm-' + this.labelsm;
36636 items.cls += ' col-sm-' + (12 - this.labelsm);
36639 if(this.labelxs > 0){
36640 label.cls += ' col-xs-' + this.labelxs;
36641 items.cls += ' col-xs-' + (12 - this.labelxs);
36647 cls : 'roo-radio-set',
36651 cls : 'roo-radio-set-input',
36654 value : this.value ? this.value : ''
36661 if(this.weight.length){
36662 cfg.cls += ' roo-radio-' + this.weight;
36666 cfg.cls += ' roo-radio-set-inline';
36670 ['xs','sm','md','lg'].map(function(size){
36671 if (settings[size]) {
36672 cfg.cls += ' col-' + size + '-' + settings[size];
36680 initEvents : function()
36682 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36683 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36685 if(!this.fieldLabel.length){
36686 this.labelEl.hide();
36689 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36690 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36692 this.indicator = this.indicatorEl();
36694 if(this.indicator){
36695 this.indicator.addClass('invisible');
36698 this.originalValue = this.getValue();
36702 inputEl: function ()
36704 return this.el.select('.roo-radio-set-input', true).first();
36707 getChildContainer : function()
36709 return this.itemsEl;
36712 register : function(item)
36714 this.radioes.push(item);
36718 validate : function()
36720 if(this.getVisibilityEl().hasClass('hidden')){
36726 Roo.each(this.radioes, function(i){
36735 if(this.allowBlank) {
36739 if(this.disabled || valid){
36744 this.markInvalid();
36749 markValid : function()
36751 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36752 this.indicatorEl().removeClass('visible');
36753 this.indicatorEl().addClass('invisible');
36757 if (Roo.bootstrap.version == 3) {
36758 this.el.removeClass([this.invalidClass, this.validClass]);
36759 this.el.addClass(this.validClass);
36761 this.el.removeClass(['is-invalid','is-valid']);
36762 this.el.addClass(['is-valid']);
36764 this.fireEvent('valid', this);
36767 markInvalid : function(msg)
36769 if(this.allowBlank || this.disabled){
36773 if(this.labelEl.isVisible(true) && this.indicatorEl()){
36774 this.indicatorEl().removeClass('invisible');
36775 this.indicatorEl().addClass('visible');
36777 if (Roo.bootstrap.version == 3) {
36778 this.el.removeClass([this.invalidClass, this.validClass]);
36779 this.el.addClass(this.invalidClass);
36781 this.el.removeClass(['is-invalid','is-valid']);
36782 this.el.addClass(['is-invalid']);
36785 this.fireEvent('invalid', this, msg);
36789 setValue : function(v, suppressEvent)
36791 if(this.value === v){
36798 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36801 Roo.each(this.radioes, function(i){
36803 i.el.removeClass('checked');
36806 Roo.each(this.radioes, function(i){
36808 if(i.value === v || i.value.toString() === v.toString()){
36810 i.el.addClass('checked');
36812 if(suppressEvent !== true){
36813 this.fireEvent('check', this, i);
36824 clearInvalid : function(){
36826 if(!this.el || this.preventMark){
36830 this.el.removeClass([this.invalidClass]);
36832 this.fireEvent('valid', this);
36837 Roo.apply(Roo.bootstrap.RadioSet, {
36841 register : function(set)
36843 this.groups[set.name] = set;
36846 get: function(name)
36848 if (typeof(this.groups[name]) == 'undefined') {
36852 return this.groups[name] ;
36858 * Ext JS Library 1.1.1
36859 * Copyright(c) 2006-2007, Ext JS, LLC.
36861 * Originally Released Under LGPL - original licence link has changed is not relivant.
36864 * <script type="text/javascript">
36869 * @class Roo.bootstrap.SplitBar
36870 * @extends Roo.util.Observable
36871 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36875 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36876 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36877 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36878 split.minSize = 100;
36879 split.maxSize = 600;
36880 split.animate = true;
36881 split.on('moved', splitterMoved);
36884 * Create a new SplitBar
36885 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
36886 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
36887 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36888 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
36889 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36890 position of the SplitBar).
36892 Roo.bootstrap.SplitBar = function(cfg){
36897 // dragElement : elm
36898 // resizingElement: el,
36900 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36901 // placement : Roo.bootstrap.SplitBar.LEFT ,
36902 // existingProxy ???
36905 this.el = Roo.get(cfg.dragElement, true);
36906 this.el.dom.unselectable = "on";
36908 this.resizingEl = Roo.get(cfg.resizingElement, true);
36912 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36913 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36916 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36919 * The minimum size of the resizing element. (Defaults to 0)
36925 * The maximum size of the resizing element. (Defaults to 2000)
36928 this.maxSize = 2000;
36931 * Whether to animate the transition to the new size
36934 this.animate = false;
36937 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36940 this.useShim = false;
36945 if(!cfg.existingProxy){
36947 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36949 this.proxy = Roo.get(cfg.existingProxy).dom;
36952 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36955 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36958 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36961 this.dragSpecs = {};
36964 * @private The adapter to use to positon and resize elements
36966 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36967 this.adapter.init(this);
36969 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36971 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36972 this.el.addClass("roo-splitbar-h");
36975 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36976 this.el.addClass("roo-splitbar-v");
36982 * Fires when the splitter is moved (alias for {@link #event-moved})
36983 * @param {Roo.bootstrap.SplitBar} this
36984 * @param {Number} newSize the new width or height
36989 * Fires when the splitter is moved
36990 * @param {Roo.bootstrap.SplitBar} this
36991 * @param {Number} newSize the new width or height
36995 * @event beforeresize
36996 * Fires before the splitter is dragged
36997 * @param {Roo.bootstrap.SplitBar} this
36999 "beforeresize" : true,
37001 "beforeapply" : true
37004 Roo.util.Observable.call(this);
37007 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37008 onStartProxyDrag : function(x, y){
37009 this.fireEvent("beforeresize", this);
37011 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37013 o.enableDisplayMode("block");
37014 // all splitbars share the same overlay
37015 Roo.bootstrap.SplitBar.prototype.overlay = o;
37017 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37018 this.overlay.show();
37019 Roo.get(this.proxy).setDisplayed("block");
37020 var size = this.adapter.getElementSize(this);
37021 this.activeMinSize = this.getMinimumSize();;
37022 this.activeMaxSize = this.getMaximumSize();;
37023 var c1 = size - this.activeMinSize;
37024 var c2 = Math.max(this.activeMaxSize - size, 0);
37025 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37026 this.dd.resetConstraints();
37027 this.dd.setXConstraint(
37028 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37029 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37031 this.dd.setYConstraint(0, 0);
37033 this.dd.resetConstraints();
37034 this.dd.setXConstraint(0, 0);
37035 this.dd.setYConstraint(
37036 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37037 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37040 this.dragSpecs.startSize = size;
37041 this.dragSpecs.startPoint = [x, y];
37042 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37046 * @private Called after the drag operation by the DDProxy
37048 onEndProxyDrag : function(e){
37049 Roo.get(this.proxy).setDisplayed(false);
37050 var endPoint = Roo.lib.Event.getXY(e);
37052 this.overlay.hide();
37055 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37056 newSize = this.dragSpecs.startSize +
37057 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37058 endPoint[0] - this.dragSpecs.startPoint[0] :
37059 this.dragSpecs.startPoint[0] - endPoint[0]
37062 newSize = this.dragSpecs.startSize +
37063 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37064 endPoint[1] - this.dragSpecs.startPoint[1] :
37065 this.dragSpecs.startPoint[1] - endPoint[1]
37068 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37069 if(newSize != this.dragSpecs.startSize){
37070 if(this.fireEvent('beforeapply', this, newSize) !== false){
37071 this.adapter.setElementSize(this, newSize);
37072 this.fireEvent("moved", this, newSize);
37073 this.fireEvent("resize", this, newSize);
37079 * Get the adapter this SplitBar uses
37080 * @return The adapter object
37082 getAdapter : function(){
37083 return this.adapter;
37087 * Set the adapter this SplitBar uses
37088 * @param {Object} adapter A SplitBar adapter object
37090 setAdapter : function(adapter){
37091 this.adapter = adapter;
37092 this.adapter.init(this);
37096 * Gets the minimum size for the resizing element
37097 * @return {Number} The minimum size
37099 getMinimumSize : function(){
37100 return this.minSize;
37104 * Sets the minimum size for the resizing element
37105 * @param {Number} minSize The minimum size
37107 setMinimumSize : function(minSize){
37108 this.minSize = minSize;
37112 * Gets the maximum size for the resizing element
37113 * @return {Number} The maximum size
37115 getMaximumSize : function(){
37116 return this.maxSize;
37120 * Sets the maximum size for the resizing element
37121 * @param {Number} maxSize The maximum size
37123 setMaximumSize : function(maxSize){
37124 this.maxSize = maxSize;
37128 * Sets the initialize size for the resizing element
37129 * @param {Number} size The initial size
37131 setCurrentSize : function(size){
37132 var oldAnimate = this.animate;
37133 this.animate = false;
37134 this.adapter.setElementSize(this, size);
37135 this.animate = oldAnimate;
37139 * Destroy this splitbar.
37140 * @param {Boolean} removeEl True to remove the element
37142 destroy : function(removeEl){
37144 this.shim.remove();
37147 this.proxy.parentNode.removeChild(this.proxy);
37155 * @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.
37157 Roo.bootstrap.SplitBar.createProxy = function(dir){
37158 var proxy = new Roo.Element(document.createElement("div"));
37159 proxy.unselectable();
37160 var cls = 'roo-splitbar-proxy';
37161 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37162 document.body.appendChild(proxy.dom);
37167 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37168 * Default Adapter. It assumes the splitter and resizing element are not positioned
37169 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37171 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37174 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37175 // do nothing for now
37176 init : function(s){
37180 * Called before drag operations to get the current size of the resizing element.
37181 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37183 getElementSize : function(s){
37184 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37185 return s.resizingEl.getWidth();
37187 return s.resizingEl.getHeight();
37192 * Called after drag operations to set the size of the resizing element.
37193 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37194 * @param {Number} newSize The new size to set
37195 * @param {Function} onComplete A function to be invoked when resizing is complete
37197 setElementSize : function(s, newSize, onComplete){
37198 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37200 s.resizingEl.setWidth(newSize);
37202 onComplete(s, newSize);
37205 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37210 s.resizingEl.setHeight(newSize);
37212 onComplete(s, newSize);
37215 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37222 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37223 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37224 * Adapter that moves the splitter element to align with the resized sizing element.
37225 * Used with an absolute positioned SplitBar.
37226 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37227 * document.body, make sure you assign an id to the body element.
37229 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37230 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37231 this.container = Roo.get(container);
37234 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37235 init : function(s){
37236 this.basic.init(s);
37239 getElementSize : function(s){
37240 return this.basic.getElementSize(s);
37243 setElementSize : function(s, newSize, onComplete){
37244 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37247 moveSplitter : function(s){
37248 var yes = Roo.bootstrap.SplitBar;
37249 switch(s.placement){
37251 s.el.setX(s.resizingEl.getRight());
37254 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37257 s.el.setY(s.resizingEl.getBottom());
37260 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37267 * Orientation constant - Create a vertical SplitBar
37271 Roo.bootstrap.SplitBar.VERTICAL = 1;
37274 * Orientation constant - Create a horizontal SplitBar
37278 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37281 * Placement constant - The resizing element is to the left of the splitter element
37285 Roo.bootstrap.SplitBar.LEFT = 1;
37288 * Placement constant - The resizing element is to the right of the splitter element
37292 Roo.bootstrap.SplitBar.RIGHT = 2;
37295 * Placement constant - The resizing element is positioned above the splitter element
37299 Roo.bootstrap.SplitBar.TOP = 3;
37302 * Placement constant - The resizing element is positioned under splitter element
37306 Roo.bootstrap.SplitBar.BOTTOM = 4;
37307 Roo.namespace("Roo.bootstrap.layout");/*
37309 * Ext JS Library 1.1.1
37310 * Copyright(c) 2006-2007, Ext JS, LLC.
37312 * Originally Released Under LGPL - original licence link has changed is not relivant.
37315 * <script type="text/javascript">
37319 * @class Roo.bootstrap.layout.Manager
37320 * @extends Roo.bootstrap.Component
37321 * Base class for layout managers.
37323 Roo.bootstrap.layout.Manager = function(config)
37325 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37331 /** false to disable window resize monitoring @type Boolean */
37332 this.monitorWindowResize = true;
37337 * Fires when a layout is performed.
37338 * @param {Roo.LayoutManager} this
37342 * @event regionresized
37343 * Fires when the user resizes a region.
37344 * @param {Roo.LayoutRegion} region The resized region
37345 * @param {Number} newSize The new size (width for east/west, height for north/south)
37347 "regionresized" : true,
37349 * @event regioncollapsed
37350 * Fires when a region is collapsed.
37351 * @param {Roo.LayoutRegion} region The collapsed region
37353 "regioncollapsed" : true,
37355 * @event regionexpanded
37356 * Fires when a region is expanded.
37357 * @param {Roo.LayoutRegion} region The expanded region
37359 "regionexpanded" : true
37361 this.updating = false;
37364 this.el = Roo.get(config.el);
37370 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37375 monitorWindowResize : true,
37381 onRender : function(ct, position)
37384 this.el = Roo.get(ct);
37387 //this.fireEvent('render',this);
37391 initEvents: function()
37395 // ie scrollbar fix
37396 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37397 document.body.scroll = "no";
37398 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37399 this.el.position('relative');
37401 this.id = this.el.id;
37402 this.el.addClass("roo-layout-container");
37403 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37404 if(this.el.dom != document.body ) {
37405 this.el.on('resize', this.layout,this);
37406 this.el.on('show', this.layout,this);
37412 * Returns true if this layout is currently being updated
37413 * @return {Boolean}
37415 isUpdating : function(){
37416 return this.updating;
37420 * Suspend the LayoutManager from doing auto-layouts while
37421 * making multiple add or remove calls
37423 beginUpdate : function(){
37424 this.updating = true;
37428 * Restore auto-layouts and optionally disable the manager from performing a layout
37429 * @param {Boolean} noLayout true to disable a layout update
37431 endUpdate : function(noLayout){
37432 this.updating = false;
37438 layout: function(){
37442 onRegionResized : function(region, newSize){
37443 this.fireEvent("regionresized", region, newSize);
37447 onRegionCollapsed : function(region){
37448 this.fireEvent("regioncollapsed", region);
37451 onRegionExpanded : function(region){
37452 this.fireEvent("regionexpanded", region);
37456 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37457 * performs box-model adjustments.
37458 * @return {Object} The size as an object {width: (the width), height: (the height)}
37460 getViewSize : function()
37463 if(this.el.dom != document.body){
37464 size = this.el.getSize();
37466 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37468 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37469 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37474 * Returns the Element this layout is bound to.
37475 * @return {Roo.Element}
37477 getEl : function(){
37482 * Returns the specified region.
37483 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37484 * @return {Roo.LayoutRegion}
37486 getRegion : function(target){
37487 return this.regions[target.toLowerCase()];
37490 onWindowResize : function(){
37491 if(this.monitorWindowResize){
37498 * Ext JS Library 1.1.1
37499 * Copyright(c) 2006-2007, Ext JS, LLC.
37501 * Originally Released Under LGPL - original licence link has changed is not relivant.
37504 * <script type="text/javascript">
37507 * @class Roo.bootstrap.layout.Border
37508 * @extends Roo.bootstrap.layout.Manager
37509 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37510 * please see: examples/bootstrap/nested.html<br><br>
37512 <b>The container the layout is rendered into can be either the body element or any other element.
37513 If it is not the body element, the container needs to either be an absolute positioned element,
37514 or you will need to add "position:relative" to the css of the container. You will also need to specify
37515 the container size if it is not the body element.</b>
37518 * Create a new Border
37519 * @param {Object} config Configuration options
37521 Roo.bootstrap.layout.Border = function(config){
37522 config = config || {};
37523 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37527 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37528 if(config[region]){
37529 config[region].region = region;
37530 this.addRegion(config[region]);
37536 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37538 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37540 parent : false, // this might point to a 'nest' or a ???
37543 * Creates and adds a new region if it doesn't already exist.
37544 * @param {String} target The target region key (north, south, east, west or center).
37545 * @param {Object} config The regions config object
37546 * @return {BorderLayoutRegion} The new region
37548 addRegion : function(config)
37550 if(!this.regions[config.region]){
37551 var r = this.factory(config);
37552 this.bindRegion(r);
37554 return this.regions[config.region];
37558 bindRegion : function(r){
37559 this.regions[r.config.region] = r;
37561 r.on("visibilitychange", this.layout, this);
37562 r.on("paneladded", this.layout, this);
37563 r.on("panelremoved", this.layout, this);
37564 r.on("invalidated", this.layout, this);
37565 r.on("resized", this.onRegionResized, this);
37566 r.on("collapsed", this.onRegionCollapsed, this);
37567 r.on("expanded", this.onRegionExpanded, this);
37571 * Performs a layout update.
37573 layout : function()
37575 if(this.updating) {
37579 // render all the rebions if they have not been done alreayd?
37580 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37581 if(this.regions[region] && !this.regions[region].bodyEl){
37582 this.regions[region].onRender(this.el)
37586 var size = this.getViewSize();
37587 var w = size.width;
37588 var h = size.height;
37593 //var x = 0, y = 0;
37595 var rs = this.regions;
37596 var north = rs["north"];
37597 var south = rs["south"];
37598 var west = rs["west"];
37599 var east = rs["east"];
37600 var center = rs["center"];
37601 //if(this.hideOnLayout){ // not supported anymore
37602 //c.el.setStyle("display", "none");
37604 if(north && north.isVisible()){
37605 var b = north.getBox();
37606 var m = north.getMargins();
37607 b.width = w - (m.left+m.right);
37610 centerY = b.height + b.y + m.bottom;
37611 centerH -= centerY;
37612 north.updateBox(this.safeBox(b));
37614 if(south && south.isVisible()){
37615 var b = south.getBox();
37616 var m = south.getMargins();
37617 b.width = w - (m.left+m.right);
37619 var totalHeight = (b.height + m.top + m.bottom);
37620 b.y = h - totalHeight + m.top;
37621 centerH -= totalHeight;
37622 south.updateBox(this.safeBox(b));
37624 if(west && west.isVisible()){
37625 var b = west.getBox();
37626 var m = west.getMargins();
37627 b.height = centerH - (m.top+m.bottom);
37629 b.y = centerY + m.top;
37630 var totalWidth = (b.width + m.left + m.right);
37631 centerX += totalWidth;
37632 centerW -= totalWidth;
37633 west.updateBox(this.safeBox(b));
37635 if(east && east.isVisible()){
37636 var b = east.getBox();
37637 var m = east.getMargins();
37638 b.height = centerH - (m.top+m.bottom);
37639 var totalWidth = (b.width + m.left + m.right);
37640 b.x = w - totalWidth + m.left;
37641 b.y = centerY + m.top;
37642 centerW -= totalWidth;
37643 east.updateBox(this.safeBox(b));
37646 var m = center.getMargins();
37648 x: centerX + m.left,
37649 y: centerY + m.top,
37650 width: centerW - (m.left+m.right),
37651 height: centerH - (m.top+m.bottom)
37653 //if(this.hideOnLayout){
37654 //center.el.setStyle("display", "block");
37656 center.updateBox(this.safeBox(centerBox));
37659 this.fireEvent("layout", this);
37663 safeBox : function(box){
37664 box.width = Math.max(0, box.width);
37665 box.height = Math.max(0, box.height);
37670 * Adds a ContentPanel (or subclass) to this layout.
37671 * @param {String} target The target region key (north, south, east, west or center).
37672 * @param {Roo.ContentPanel} panel The panel to add
37673 * @return {Roo.ContentPanel} The added panel
37675 add : function(target, panel){
37677 target = target.toLowerCase();
37678 return this.regions[target].add(panel);
37682 * Remove a ContentPanel (or subclass) to this layout.
37683 * @param {String} target The target region key (north, south, east, west or center).
37684 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37685 * @return {Roo.ContentPanel} The removed panel
37687 remove : function(target, panel){
37688 target = target.toLowerCase();
37689 return this.regions[target].remove(panel);
37693 * Searches all regions for a panel with the specified id
37694 * @param {String} panelId
37695 * @return {Roo.ContentPanel} The panel or null if it wasn't found
37697 findPanel : function(panelId){
37698 var rs = this.regions;
37699 for(var target in rs){
37700 if(typeof rs[target] != "function"){
37701 var p = rs[target].getPanel(panelId);
37711 * Searches all regions for a panel with the specified id and activates (shows) it.
37712 * @param {String/ContentPanel} panelId The panels id or the panel itself
37713 * @return {Roo.ContentPanel} The shown panel or null
37715 showPanel : function(panelId) {
37716 var rs = this.regions;
37717 for(var target in rs){
37718 var r = rs[target];
37719 if(typeof r != "function"){
37720 if(r.hasPanel(panelId)){
37721 return r.showPanel(panelId);
37729 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37730 * @param {Roo.state.Provider} provider (optional) An alternate state provider
37733 restoreState : function(provider){
37735 provider = Roo.state.Manager;
37737 var sm = new Roo.LayoutStateManager();
37738 sm.init(this, provider);
37744 * Adds a xtype elements to the layout.
37748 xtype : 'ContentPanel',
37755 xtype : 'NestedLayoutPanel',
37761 items : [ ... list of content panels or nested layout panels.. ]
37765 * @param {Object} cfg Xtype definition of item to add.
37767 addxtype : function(cfg)
37769 // basically accepts a pannel...
37770 // can accept a layout region..!?!?
37771 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37774 // theory? children can only be panels??
37776 //if (!cfg.xtype.match(/Panel$/)) {
37781 if (typeof(cfg.region) == 'undefined') {
37782 Roo.log("Failed to add Panel, region was not set");
37786 var region = cfg.region;
37792 xitems = cfg.items;
37797 if ( region == 'center') {
37798 Roo.log("Center: " + cfg.title);
37804 case 'Content': // ContentPanel (el, cfg)
37805 case 'Scroll': // ContentPanel (el, cfg)
37807 cfg.autoCreate = cfg.autoCreate || true;
37808 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37810 // var el = this.el.createChild();
37811 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37814 this.add(region, ret);
37818 case 'TreePanel': // our new panel!
37819 cfg.el = this.el.createChild();
37820 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37821 this.add(region, ret);
37826 // create a new Layout (which is a Border Layout...
37828 var clayout = cfg.layout;
37829 clayout.el = this.el.createChild();
37830 clayout.items = clayout.items || [];
37834 // replace this exitems with the clayout ones..
37835 xitems = clayout.items;
37837 // force background off if it's in center...
37838 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37839 cfg.background = false;
37841 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
37844 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37845 //console.log('adding nested layout panel ' + cfg.toSource());
37846 this.add(region, ret);
37847 nb = {}; /// find first...
37852 // needs grid and region
37854 //var el = this.getRegion(region).el.createChild();
37856 *var el = this.el.createChild();
37857 // create the grid first...
37858 cfg.grid.container = el;
37859 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37862 if (region == 'center' && this.active ) {
37863 cfg.background = false;
37866 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37868 this.add(region, ret);
37870 if (cfg.background) {
37871 // render grid on panel activation (if panel background)
37872 ret.on('activate', function(gp) {
37873 if (!gp.grid.rendered) {
37874 // gp.grid.render(el);
37878 // cfg.grid.render(el);
37884 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37885 // it was the old xcomponent building that caused this before.
37886 // espeically if border is the top element in the tree.
37896 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37898 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37899 this.add(region, ret);
37903 throw "Can not add '" + cfg.xtype + "' to Border";
37909 this.beginUpdate();
37913 Roo.each(xitems, function(i) {
37914 region = nb && i.region ? i.region : false;
37916 var add = ret.addxtype(i);
37919 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37920 if (!i.background) {
37921 abn[region] = nb[region] ;
37928 // make the last non-background panel active..
37929 //if (nb) { Roo.log(abn); }
37932 for(var r in abn) {
37933 region = this.getRegion(r);
37935 // tried using nb[r], but it does not work..
37937 region.showPanel(abn[r]);
37948 factory : function(cfg)
37951 var validRegions = Roo.bootstrap.layout.Border.regions;
37953 var target = cfg.region;
37956 var r = Roo.bootstrap.layout;
37960 return new r.North(cfg);
37962 return new r.South(cfg);
37964 return new r.East(cfg);
37966 return new r.West(cfg);
37968 return new r.Center(cfg);
37970 throw 'Layout region "'+target+'" not supported.';
37977 * Ext JS Library 1.1.1
37978 * Copyright(c) 2006-2007, Ext JS, LLC.
37980 * Originally Released Under LGPL - original licence link has changed is not relivant.
37983 * <script type="text/javascript">
37987 * @class Roo.bootstrap.layout.Basic
37988 * @extends Roo.util.Observable
37989 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37990 * and does not have a titlebar, tabs or any other features. All it does is size and position
37991 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37992 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
37993 * @cfg {string} region the region that it inhabits..
37994 * @cfg {bool} skipConfig skip config?
37998 Roo.bootstrap.layout.Basic = function(config){
38000 this.mgr = config.mgr;
38002 this.position = config.region;
38004 var skipConfig = config.skipConfig;
38008 * @scope Roo.BasicLayoutRegion
38012 * @event beforeremove
38013 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38014 * @param {Roo.LayoutRegion} this
38015 * @param {Roo.ContentPanel} panel The panel
38016 * @param {Object} e The cancel event object
38018 "beforeremove" : true,
38020 * @event invalidated
38021 * Fires when the layout for this region is changed.
38022 * @param {Roo.LayoutRegion} this
38024 "invalidated" : true,
38026 * @event visibilitychange
38027 * Fires when this region is shown or hidden
38028 * @param {Roo.LayoutRegion} this
38029 * @param {Boolean} visibility true or false
38031 "visibilitychange" : true,
38033 * @event paneladded
38034 * Fires when a panel is added.
38035 * @param {Roo.LayoutRegion} this
38036 * @param {Roo.ContentPanel} panel The panel
38038 "paneladded" : true,
38040 * @event panelremoved
38041 * Fires when a panel is removed.
38042 * @param {Roo.LayoutRegion} this
38043 * @param {Roo.ContentPanel} panel The panel
38045 "panelremoved" : true,
38047 * @event beforecollapse
38048 * Fires when this region before collapse.
38049 * @param {Roo.LayoutRegion} this
38051 "beforecollapse" : true,
38054 * Fires when this region is collapsed.
38055 * @param {Roo.LayoutRegion} this
38057 "collapsed" : true,
38060 * Fires when this region is expanded.
38061 * @param {Roo.LayoutRegion} this
38066 * Fires when this region is slid into view.
38067 * @param {Roo.LayoutRegion} this
38069 "slideshow" : true,
38072 * Fires when this region slides out of view.
38073 * @param {Roo.LayoutRegion} this
38075 "slidehide" : true,
38077 * @event panelactivated
38078 * Fires when a panel is activated.
38079 * @param {Roo.LayoutRegion} this
38080 * @param {Roo.ContentPanel} panel The activated panel
38082 "panelactivated" : true,
38085 * Fires when the user resizes this region.
38086 * @param {Roo.LayoutRegion} this
38087 * @param {Number} newSize The new size (width for east/west, height for north/south)
38091 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38092 this.panels = new Roo.util.MixedCollection();
38093 this.panels.getKey = this.getPanelId.createDelegate(this);
38095 this.activePanel = null;
38096 // ensure listeners are added...
38098 if (config.listeners || config.events) {
38099 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38100 listeners : config.listeners || {},
38101 events : config.events || {}
38105 if(skipConfig !== true){
38106 this.applyConfig(config);
38110 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38112 getPanelId : function(p){
38116 applyConfig : function(config){
38117 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38118 this.config = config;
38123 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38124 * the width, for horizontal (north, south) the height.
38125 * @param {Number} newSize The new width or height
38127 resizeTo : function(newSize){
38128 var el = this.el ? this.el :
38129 (this.activePanel ? this.activePanel.getEl() : null);
38131 switch(this.position){
38134 el.setWidth(newSize);
38135 this.fireEvent("resized", this, newSize);
38139 el.setHeight(newSize);
38140 this.fireEvent("resized", this, newSize);
38146 getBox : function(){
38147 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38150 getMargins : function(){
38151 return this.margins;
38154 updateBox : function(box){
38156 var el = this.activePanel.getEl();
38157 el.dom.style.left = box.x + "px";
38158 el.dom.style.top = box.y + "px";
38159 this.activePanel.setSize(box.width, box.height);
38163 * Returns the container element for this region.
38164 * @return {Roo.Element}
38166 getEl : function(){
38167 return this.activePanel;
38171 * Returns true if this region is currently visible.
38172 * @return {Boolean}
38174 isVisible : function(){
38175 return this.activePanel ? true : false;
38178 setActivePanel : function(panel){
38179 panel = this.getPanel(panel);
38180 if(this.activePanel && this.activePanel != panel){
38181 this.activePanel.setActiveState(false);
38182 this.activePanel.getEl().setLeftTop(-10000,-10000);
38184 this.activePanel = panel;
38185 panel.setActiveState(true);
38187 panel.setSize(this.box.width, this.box.height);
38189 this.fireEvent("panelactivated", this, panel);
38190 this.fireEvent("invalidated");
38194 * Show the specified panel.
38195 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38196 * @return {Roo.ContentPanel} The shown panel or null
38198 showPanel : function(panel){
38199 panel = this.getPanel(panel);
38201 this.setActivePanel(panel);
38207 * Get the active panel for this region.
38208 * @return {Roo.ContentPanel} The active panel or null
38210 getActivePanel : function(){
38211 return this.activePanel;
38215 * Add the passed ContentPanel(s)
38216 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38217 * @return {Roo.ContentPanel} The panel added (if only one was added)
38219 add : function(panel){
38220 if(arguments.length > 1){
38221 for(var i = 0, len = arguments.length; i < len; i++) {
38222 this.add(arguments[i]);
38226 if(this.hasPanel(panel)){
38227 this.showPanel(panel);
38230 var el = panel.getEl();
38231 if(el.dom.parentNode != this.mgr.el.dom){
38232 this.mgr.el.dom.appendChild(el.dom);
38234 if(panel.setRegion){
38235 panel.setRegion(this);
38237 this.panels.add(panel);
38238 el.setStyle("position", "absolute");
38239 if(!panel.background){
38240 this.setActivePanel(panel);
38241 if(this.config.initialSize && this.panels.getCount()==1){
38242 this.resizeTo(this.config.initialSize);
38245 this.fireEvent("paneladded", this, panel);
38250 * Returns true if the panel is in this region.
38251 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38252 * @return {Boolean}
38254 hasPanel : function(panel){
38255 if(typeof panel == "object"){ // must be panel obj
38256 panel = panel.getId();
38258 return this.getPanel(panel) ? true : false;
38262 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38263 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38264 * @param {Boolean} preservePanel Overrides the config preservePanel option
38265 * @return {Roo.ContentPanel} The panel that was removed
38267 remove : function(panel, preservePanel){
38268 panel = this.getPanel(panel);
38273 this.fireEvent("beforeremove", this, panel, e);
38274 if(e.cancel === true){
38277 var panelId = panel.getId();
38278 this.panels.removeKey(panelId);
38283 * Returns the panel specified or null if it's not in this region.
38284 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38285 * @return {Roo.ContentPanel}
38287 getPanel : function(id){
38288 if(typeof id == "object"){ // must be panel obj
38291 return this.panels.get(id);
38295 * Returns this regions position (north/south/east/west/center).
38298 getPosition: function(){
38299 return this.position;
38303 * Ext JS Library 1.1.1
38304 * Copyright(c) 2006-2007, Ext JS, LLC.
38306 * Originally Released Under LGPL - original licence link has changed is not relivant.
38309 * <script type="text/javascript">
38313 * @class Roo.bootstrap.layout.Region
38314 * @extends Roo.bootstrap.layout.Basic
38315 * This class represents a region in a layout manager.
38317 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38318 * @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})
38319 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38320 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38321 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38322 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38323 * @cfg {String} title The title for the region (overrides panel titles)
38324 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38325 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38326 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38327 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38328 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38329 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38330 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38331 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38332 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38333 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38335 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38336 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38337 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38338 * @cfg {Number} width For East/West panels
38339 * @cfg {Number} height For North/South panels
38340 * @cfg {Boolean} split To show the splitter
38341 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38343 * @cfg {string} cls Extra CSS classes to add to region
38345 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38346 * @cfg {string} region the region that it inhabits..
38349 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38350 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38352 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38353 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38354 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38356 Roo.bootstrap.layout.Region = function(config)
38358 this.applyConfig(config);
38360 var mgr = config.mgr;
38361 var pos = config.region;
38362 config.skipConfig = true;
38363 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38366 this.onRender(mgr.el);
38369 this.visible = true;
38370 this.collapsed = false;
38371 this.unrendered_panels = [];
38374 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38376 position: '', // set by wrapper (eg. north/south etc..)
38377 unrendered_panels : null, // unrendered panels.
38379 tabPosition : false,
38381 mgr: false, // points to 'Border'
38384 createBody : function(){
38385 /** This region's body element
38386 * @type Roo.Element */
38387 this.bodyEl = this.el.createChild({
38389 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38393 onRender: function(ctr, pos)
38395 var dh = Roo.DomHelper;
38396 /** This region's container element
38397 * @type Roo.Element */
38398 this.el = dh.append(ctr.dom, {
38400 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38402 /** This region's title element
38403 * @type Roo.Element */
38405 this.titleEl = dh.append(this.el.dom, {
38407 unselectable: "on",
38408 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38410 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38411 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38415 this.titleEl.enableDisplayMode();
38416 /** This region's title text element
38417 * @type HTMLElement */
38418 this.titleTextEl = this.titleEl.dom.firstChild;
38419 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38421 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38422 this.closeBtn.enableDisplayMode();
38423 this.closeBtn.on("click", this.closeClicked, this);
38424 this.closeBtn.hide();
38426 this.createBody(this.config);
38427 if(this.config.hideWhenEmpty){
38429 this.on("paneladded", this.validateVisibility, this);
38430 this.on("panelremoved", this.validateVisibility, this);
38432 if(this.autoScroll){
38433 this.bodyEl.setStyle("overflow", "auto");
38435 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38437 //if(c.titlebar !== false){
38438 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38439 this.titleEl.hide();
38441 this.titleEl.show();
38442 if(this.config.title){
38443 this.titleTextEl.innerHTML = this.config.title;
38447 if(this.config.collapsed){
38448 this.collapse(true);
38450 if(this.config.hidden){
38454 if (this.unrendered_panels && this.unrendered_panels.length) {
38455 for (var i =0;i< this.unrendered_panels.length; i++) {
38456 this.add(this.unrendered_panels[i]);
38458 this.unrendered_panels = null;
38464 applyConfig : function(c)
38467 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38468 var dh = Roo.DomHelper;
38469 if(c.titlebar !== false){
38470 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38471 this.collapseBtn.on("click", this.collapse, this);
38472 this.collapseBtn.enableDisplayMode();
38474 if(c.showPin === true || this.showPin){
38475 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38476 this.stickBtn.enableDisplayMode();
38477 this.stickBtn.on("click", this.expand, this);
38478 this.stickBtn.hide();
38483 /** This region's collapsed element
38484 * @type Roo.Element */
38487 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38488 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38491 if(c.floatable !== false){
38492 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38493 this.collapsedEl.on("click", this.collapseClick, this);
38496 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38497 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38498 id: "message", unselectable: "on", style:{"float":"left"}});
38499 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38501 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38502 this.expandBtn.on("click", this.expand, this);
38506 if(this.collapseBtn){
38507 this.collapseBtn.setVisible(c.collapsible == true);
38510 this.cmargins = c.cmargins || this.cmargins ||
38511 (this.position == "west" || this.position == "east" ?
38512 {top: 0, left: 2, right:2, bottom: 0} :
38513 {top: 2, left: 0, right:0, bottom: 2});
38515 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38518 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38520 this.autoScroll = c.autoScroll || false;
38525 this.duration = c.duration || .30;
38526 this.slideDuration = c.slideDuration || .45;
38531 * Returns true if this region is currently visible.
38532 * @return {Boolean}
38534 isVisible : function(){
38535 return this.visible;
38539 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38540 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38542 //setCollapsedTitle : function(title){
38543 // title = title || " ";
38544 // if(this.collapsedTitleTextEl){
38545 // this.collapsedTitleTextEl.innerHTML = title;
38549 getBox : function(){
38551 // if(!this.collapsed){
38552 b = this.el.getBox(false, true);
38554 // b = this.collapsedEl.getBox(false, true);
38559 getMargins : function(){
38560 return this.margins;
38561 //return this.collapsed ? this.cmargins : this.margins;
38564 highlight : function(){
38565 this.el.addClass("x-layout-panel-dragover");
38568 unhighlight : function(){
38569 this.el.removeClass("x-layout-panel-dragover");
38572 updateBox : function(box)
38574 if (!this.bodyEl) {
38575 return; // not rendered yet..
38579 if(!this.collapsed){
38580 this.el.dom.style.left = box.x + "px";
38581 this.el.dom.style.top = box.y + "px";
38582 this.updateBody(box.width, box.height);
38584 this.collapsedEl.dom.style.left = box.x + "px";
38585 this.collapsedEl.dom.style.top = box.y + "px";
38586 this.collapsedEl.setSize(box.width, box.height);
38589 this.tabs.autoSizeTabs();
38593 updateBody : function(w, h)
38596 this.el.setWidth(w);
38597 w -= this.el.getBorderWidth("rl");
38598 if(this.config.adjustments){
38599 w += this.config.adjustments[0];
38602 if(h !== null && h > 0){
38603 this.el.setHeight(h);
38604 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38605 h -= this.el.getBorderWidth("tb");
38606 if(this.config.adjustments){
38607 h += this.config.adjustments[1];
38609 this.bodyEl.setHeight(h);
38611 h = this.tabs.syncHeight(h);
38614 if(this.panelSize){
38615 w = w !== null ? w : this.panelSize.width;
38616 h = h !== null ? h : this.panelSize.height;
38618 if(this.activePanel){
38619 var el = this.activePanel.getEl();
38620 w = w !== null ? w : el.getWidth();
38621 h = h !== null ? h : el.getHeight();
38622 this.panelSize = {width: w, height: h};
38623 this.activePanel.setSize(w, h);
38625 if(Roo.isIE && this.tabs){
38626 this.tabs.el.repaint();
38631 * Returns the container element for this region.
38632 * @return {Roo.Element}
38634 getEl : function(){
38639 * Hides this region.
38642 //if(!this.collapsed){
38643 this.el.dom.style.left = "-2000px";
38646 // this.collapsedEl.dom.style.left = "-2000px";
38647 // this.collapsedEl.hide();
38649 this.visible = false;
38650 this.fireEvent("visibilitychange", this, false);
38654 * Shows this region if it was previously hidden.
38657 //if(!this.collapsed){
38660 // this.collapsedEl.show();
38662 this.visible = true;
38663 this.fireEvent("visibilitychange", this, true);
38666 closeClicked : function(){
38667 if(this.activePanel){
38668 this.remove(this.activePanel);
38672 collapseClick : function(e){
38674 e.stopPropagation();
38677 e.stopPropagation();
38683 * Collapses this region.
38684 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38687 collapse : function(skipAnim, skipCheck = false){
38688 if(this.collapsed) {
38692 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38694 this.collapsed = true;
38696 this.split.el.hide();
38698 if(this.config.animate && skipAnim !== true){
38699 this.fireEvent("invalidated", this);
38700 this.animateCollapse();
38702 this.el.setLocation(-20000,-20000);
38704 this.collapsedEl.show();
38705 this.fireEvent("collapsed", this);
38706 this.fireEvent("invalidated", this);
38712 animateCollapse : function(){
38717 * Expands this region if it was previously collapsed.
38718 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38719 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38722 expand : function(e, skipAnim){
38724 e.stopPropagation();
38726 if(!this.collapsed || this.el.hasActiveFx()) {
38730 this.afterSlideIn();
38733 this.collapsed = false;
38734 if(this.config.animate && skipAnim !== true){
38735 this.animateExpand();
38739 this.split.el.show();
38741 this.collapsedEl.setLocation(-2000,-2000);
38742 this.collapsedEl.hide();
38743 this.fireEvent("invalidated", this);
38744 this.fireEvent("expanded", this);
38748 animateExpand : function(){
38752 initTabs : function()
38754 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38756 var ts = new Roo.bootstrap.panel.Tabs({
38757 el: this.bodyEl.dom,
38759 tabPosition: this.tabPosition ? this.tabPosition : 'top',
38760 disableTooltips: this.config.disableTabTips,
38761 toolbar : this.config.toolbar
38764 if(this.config.hideTabs){
38765 ts.stripWrap.setDisplayed(false);
38768 ts.resizeTabs = this.config.resizeTabs === true;
38769 ts.minTabWidth = this.config.minTabWidth || 40;
38770 ts.maxTabWidth = this.config.maxTabWidth || 250;
38771 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38772 ts.monitorResize = false;
38773 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38774 ts.bodyEl.addClass('roo-layout-tabs-body');
38775 this.panels.each(this.initPanelAsTab, this);
38778 initPanelAsTab : function(panel){
38779 var ti = this.tabs.addTab(
38783 this.config.closeOnTab && panel.isClosable(),
38786 if(panel.tabTip !== undefined){
38787 ti.setTooltip(panel.tabTip);
38789 ti.on("activate", function(){
38790 this.setActivePanel(panel);
38793 if(this.config.closeOnTab){
38794 ti.on("beforeclose", function(t, e){
38796 this.remove(panel);
38800 panel.tabItem = ti;
38805 updatePanelTitle : function(panel, title)
38807 if(this.activePanel == panel){
38808 this.updateTitle(title);
38811 var ti = this.tabs.getTab(panel.getEl().id);
38813 if(panel.tabTip !== undefined){
38814 ti.setTooltip(panel.tabTip);
38819 updateTitle : function(title){
38820 if(this.titleTextEl && !this.config.title){
38821 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
38825 setActivePanel : function(panel)
38827 panel = this.getPanel(panel);
38828 if(this.activePanel && this.activePanel != panel){
38829 if(this.activePanel.setActiveState(false) === false){
38833 this.activePanel = panel;
38834 panel.setActiveState(true);
38835 if(this.panelSize){
38836 panel.setSize(this.panelSize.width, this.panelSize.height);
38839 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38841 this.updateTitle(panel.getTitle());
38843 this.fireEvent("invalidated", this);
38845 this.fireEvent("panelactivated", this, panel);
38849 * Shows the specified panel.
38850 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38851 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38853 showPanel : function(panel)
38855 panel = this.getPanel(panel);
38858 var tab = this.tabs.getTab(panel.getEl().id);
38859 if(tab.isHidden()){
38860 this.tabs.unhideTab(tab.id);
38864 this.setActivePanel(panel);
38871 * Get the active panel for this region.
38872 * @return {Roo.ContentPanel} The active panel or null
38874 getActivePanel : function(){
38875 return this.activePanel;
38878 validateVisibility : function(){
38879 if(this.panels.getCount() < 1){
38880 this.updateTitle(" ");
38881 this.closeBtn.hide();
38884 if(!this.isVisible()){
38891 * Adds the passed ContentPanel(s) to this region.
38892 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38893 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38895 add : function(panel)
38897 if(arguments.length > 1){
38898 for(var i = 0, len = arguments.length; i < len; i++) {
38899 this.add(arguments[i]);
38904 // if we have not been rendered yet, then we can not really do much of this..
38905 if (!this.bodyEl) {
38906 this.unrendered_panels.push(panel);
38913 if(this.hasPanel(panel)){
38914 this.showPanel(panel);
38917 panel.setRegion(this);
38918 this.panels.add(panel);
38919 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38920 // sinle panel - no tab...?? would it not be better to render it with the tabs,
38921 // and hide them... ???
38922 this.bodyEl.dom.appendChild(panel.getEl().dom);
38923 if(panel.background !== true){
38924 this.setActivePanel(panel);
38926 this.fireEvent("paneladded", this, panel);
38933 this.initPanelAsTab(panel);
38937 if(panel.background !== true){
38938 this.tabs.activate(panel.getEl().id);
38940 this.fireEvent("paneladded", this, panel);
38945 * Hides the tab for the specified panel.
38946 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38948 hidePanel : function(panel){
38949 if(this.tabs && (panel = this.getPanel(panel))){
38950 this.tabs.hideTab(panel.getEl().id);
38955 * Unhides the tab for a previously hidden panel.
38956 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38958 unhidePanel : function(panel){
38959 if(this.tabs && (panel = this.getPanel(panel))){
38960 this.tabs.unhideTab(panel.getEl().id);
38964 clearPanels : function(){
38965 while(this.panels.getCount() > 0){
38966 this.remove(this.panels.first());
38971 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38972 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38973 * @param {Boolean} preservePanel Overrides the config preservePanel option
38974 * @return {Roo.ContentPanel} The panel that was removed
38976 remove : function(panel, preservePanel)
38978 panel = this.getPanel(panel);
38983 this.fireEvent("beforeremove", this, panel, e);
38984 if(e.cancel === true){
38987 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38988 var panelId = panel.getId();
38989 this.panels.removeKey(panelId);
38991 document.body.appendChild(panel.getEl().dom);
38994 this.tabs.removeTab(panel.getEl().id);
38995 }else if (!preservePanel){
38996 this.bodyEl.dom.removeChild(panel.getEl().dom);
38998 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38999 var p = this.panels.first();
39000 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39001 tempEl.appendChild(p.getEl().dom);
39002 this.bodyEl.update("");
39003 this.bodyEl.dom.appendChild(p.getEl().dom);
39005 this.updateTitle(p.getTitle());
39007 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39008 this.setActivePanel(p);
39010 panel.setRegion(null);
39011 if(this.activePanel == panel){
39012 this.activePanel = null;
39014 if(this.config.autoDestroy !== false && preservePanel !== true){
39015 try{panel.destroy();}catch(e){}
39017 this.fireEvent("panelremoved", this, panel);
39022 * Returns the TabPanel component used by this region
39023 * @return {Roo.TabPanel}
39025 getTabs : function(){
39029 createTool : function(parentEl, className){
39030 var btn = Roo.DomHelper.append(parentEl, {
39032 cls: "x-layout-tools-button",
39035 cls: "roo-layout-tools-button-inner " + className,
39039 btn.addClassOnOver("roo-layout-tools-button-over");
39044 * Ext JS Library 1.1.1
39045 * Copyright(c) 2006-2007, Ext JS, LLC.
39047 * Originally Released Under LGPL - original licence link has changed is not relivant.
39050 * <script type="text/javascript">
39056 * @class Roo.SplitLayoutRegion
39057 * @extends Roo.LayoutRegion
39058 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39060 Roo.bootstrap.layout.Split = function(config){
39061 this.cursor = config.cursor;
39062 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39065 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39067 splitTip : "Drag to resize.",
39068 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39069 useSplitTips : false,
39071 applyConfig : function(config){
39072 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39075 onRender : function(ctr,pos) {
39077 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39078 if(!this.config.split){
39083 var splitEl = Roo.DomHelper.append(ctr.dom, {
39085 id: this.el.id + "-split",
39086 cls: "roo-layout-split roo-layout-split-"+this.position,
39089 /** The SplitBar for this region
39090 * @type Roo.SplitBar */
39091 // does not exist yet...
39092 Roo.log([this.position, this.orientation]);
39094 this.split = new Roo.bootstrap.SplitBar({
39095 dragElement : splitEl,
39096 resizingElement: this.el,
39097 orientation : this.orientation
39100 this.split.on("moved", this.onSplitMove, this);
39101 this.split.useShim = this.config.useShim === true;
39102 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39103 if(this.useSplitTips){
39104 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39106 //if(config.collapsible){
39107 // this.split.el.on("dblclick", this.collapse, this);
39110 if(typeof this.config.minSize != "undefined"){
39111 this.split.minSize = this.config.minSize;
39113 if(typeof this.config.maxSize != "undefined"){
39114 this.split.maxSize = this.config.maxSize;
39116 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39117 this.hideSplitter();
39122 getHMaxSize : function(){
39123 var cmax = this.config.maxSize || 10000;
39124 var center = this.mgr.getRegion("center");
39125 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39128 getVMaxSize : function(){
39129 var cmax = this.config.maxSize || 10000;
39130 var center = this.mgr.getRegion("center");
39131 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39134 onSplitMove : function(split, newSize){
39135 this.fireEvent("resized", this, newSize);
39139 * Returns the {@link Roo.SplitBar} for this region.
39140 * @return {Roo.SplitBar}
39142 getSplitBar : function(){
39147 this.hideSplitter();
39148 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39151 hideSplitter : function(){
39153 this.split.el.setLocation(-2000,-2000);
39154 this.split.el.hide();
39160 this.split.el.show();
39162 Roo.bootstrap.layout.Split.superclass.show.call(this);
39165 beforeSlide: function(){
39166 if(Roo.isGecko){// firefox overflow auto bug workaround
39167 this.bodyEl.clip();
39169 this.tabs.bodyEl.clip();
39171 if(this.activePanel){
39172 this.activePanel.getEl().clip();
39174 if(this.activePanel.beforeSlide){
39175 this.activePanel.beforeSlide();
39181 afterSlide : function(){
39182 if(Roo.isGecko){// firefox overflow auto bug workaround
39183 this.bodyEl.unclip();
39185 this.tabs.bodyEl.unclip();
39187 if(this.activePanel){
39188 this.activePanel.getEl().unclip();
39189 if(this.activePanel.afterSlide){
39190 this.activePanel.afterSlide();
39196 initAutoHide : function(){
39197 if(this.autoHide !== false){
39198 if(!this.autoHideHd){
39199 var st = new Roo.util.DelayedTask(this.slideIn, this);
39200 this.autoHideHd = {
39201 "mouseout": function(e){
39202 if(!e.within(this.el, true)){
39206 "mouseover" : function(e){
39212 this.el.on(this.autoHideHd);
39216 clearAutoHide : function(){
39217 if(this.autoHide !== false){
39218 this.el.un("mouseout", this.autoHideHd.mouseout);
39219 this.el.un("mouseover", this.autoHideHd.mouseover);
39223 clearMonitor : function(){
39224 Roo.get(document).un("click", this.slideInIf, this);
39227 // these names are backwards but not changed for compat
39228 slideOut : function(){
39229 if(this.isSlid || this.el.hasActiveFx()){
39232 this.isSlid = true;
39233 if(this.collapseBtn){
39234 this.collapseBtn.hide();
39236 this.closeBtnState = this.closeBtn.getStyle('display');
39237 this.closeBtn.hide();
39239 this.stickBtn.show();
39242 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39243 this.beforeSlide();
39244 this.el.setStyle("z-index", 10001);
39245 this.el.slideIn(this.getSlideAnchor(), {
39246 callback: function(){
39248 this.initAutoHide();
39249 Roo.get(document).on("click", this.slideInIf, this);
39250 this.fireEvent("slideshow", this);
39257 afterSlideIn : function(){
39258 this.clearAutoHide();
39259 this.isSlid = false;
39260 this.clearMonitor();
39261 this.el.setStyle("z-index", "");
39262 if(this.collapseBtn){
39263 this.collapseBtn.show();
39265 this.closeBtn.setStyle('display', this.closeBtnState);
39267 this.stickBtn.hide();
39269 this.fireEvent("slidehide", this);
39272 slideIn : function(cb){
39273 if(!this.isSlid || this.el.hasActiveFx()){
39277 this.isSlid = false;
39278 this.beforeSlide();
39279 this.el.slideOut(this.getSlideAnchor(), {
39280 callback: function(){
39281 this.el.setLeftTop(-10000, -10000);
39283 this.afterSlideIn();
39291 slideInIf : function(e){
39292 if(!e.within(this.el)){
39297 animateCollapse : function(){
39298 this.beforeSlide();
39299 this.el.setStyle("z-index", 20000);
39300 var anchor = this.getSlideAnchor();
39301 this.el.slideOut(anchor, {
39302 callback : function(){
39303 this.el.setStyle("z-index", "");
39304 this.collapsedEl.slideIn(anchor, {duration:.3});
39306 this.el.setLocation(-10000,-10000);
39308 this.fireEvent("collapsed", this);
39315 animateExpand : function(){
39316 this.beforeSlide();
39317 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39318 this.el.setStyle("z-index", 20000);
39319 this.collapsedEl.hide({
39322 this.el.slideIn(this.getSlideAnchor(), {
39323 callback : function(){
39324 this.el.setStyle("z-index", "");
39327 this.split.el.show();
39329 this.fireEvent("invalidated", this);
39330 this.fireEvent("expanded", this);
39358 getAnchor : function(){
39359 return this.anchors[this.position];
39362 getCollapseAnchor : function(){
39363 return this.canchors[this.position];
39366 getSlideAnchor : function(){
39367 return this.sanchors[this.position];
39370 getAlignAdj : function(){
39371 var cm = this.cmargins;
39372 switch(this.position){
39388 getExpandAdj : function(){
39389 var c = this.collapsedEl, cm = this.cmargins;
39390 switch(this.position){
39392 return [-(cm.right+c.getWidth()+cm.left), 0];
39395 return [cm.right+c.getWidth()+cm.left, 0];
39398 return [0, -(cm.top+cm.bottom+c.getHeight())];
39401 return [0, cm.top+cm.bottom+c.getHeight()];
39407 * Ext JS Library 1.1.1
39408 * Copyright(c) 2006-2007, Ext JS, LLC.
39410 * Originally Released Under LGPL - original licence link has changed is not relivant.
39413 * <script type="text/javascript">
39416 * These classes are private internal classes
39418 Roo.bootstrap.layout.Center = function(config){
39419 config.region = "center";
39420 Roo.bootstrap.layout.Region.call(this, config);
39421 this.visible = true;
39422 this.minWidth = config.minWidth || 20;
39423 this.minHeight = config.minHeight || 20;
39426 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39428 // center panel can't be hidden
39432 // center panel can't be hidden
39435 getMinWidth: function(){
39436 return this.minWidth;
39439 getMinHeight: function(){
39440 return this.minHeight;
39454 Roo.bootstrap.layout.North = function(config)
39456 config.region = 'north';
39457 config.cursor = 'n-resize';
39459 Roo.bootstrap.layout.Split.call(this, config);
39463 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39464 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39465 this.split.el.addClass("roo-layout-split-v");
39467 //var size = config.initialSize || config.height;
39468 //if(this.el && typeof size != "undefined"){
39469 // this.el.setHeight(size);
39472 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39474 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39477 onRender : function(ctr, pos)
39479 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39480 var size = this.config.initialSize || this.config.height;
39481 if(this.el && typeof size != "undefined"){
39482 this.el.setHeight(size);
39487 getBox : function(){
39488 if(this.collapsed){
39489 return this.collapsedEl.getBox();
39491 var box = this.el.getBox();
39493 box.height += this.split.el.getHeight();
39498 updateBox : function(box){
39499 if(this.split && !this.collapsed){
39500 box.height -= this.split.el.getHeight();
39501 this.split.el.setLeft(box.x);
39502 this.split.el.setTop(box.y+box.height);
39503 this.split.el.setWidth(box.width);
39505 if(this.collapsed){
39506 this.updateBody(box.width, null);
39508 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39516 Roo.bootstrap.layout.South = function(config){
39517 config.region = 'south';
39518 config.cursor = 's-resize';
39519 Roo.bootstrap.layout.Split.call(this, config);
39521 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39522 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39523 this.split.el.addClass("roo-layout-split-v");
39528 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39529 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39531 onRender : function(ctr, pos)
39533 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39534 var size = this.config.initialSize || this.config.height;
39535 if(this.el && typeof size != "undefined"){
39536 this.el.setHeight(size);
39541 getBox : function(){
39542 if(this.collapsed){
39543 return this.collapsedEl.getBox();
39545 var box = this.el.getBox();
39547 var sh = this.split.el.getHeight();
39554 updateBox : function(box){
39555 if(this.split && !this.collapsed){
39556 var sh = this.split.el.getHeight();
39559 this.split.el.setLeft(box.x);
39560 this.split.el.setTop(box.y-sh);
39561 this.split.el.setWidth(box.width);
39563 if(this.collapsed){
39564 this.updateBody(box.width, null);
39566 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39570 Roo.bootstrap.layout.East = function(config){
39571 config.region = "east";
39572 config.cursor = "e-resize";
39573 Roo.bootstrap.layout.Split.call(this, config);
39575 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39576 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39577 this.split.el.addClass("roo-layout-split-h");
39581 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39582 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39584 onRender : function(ctr, pos)
39586 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39587 var size = this.config.initialSize || this.config.width;
39588 if(this.el && typeof size != "undefined"){
39589 this.el.setWidth(size);
39594 getBox : function(){
39595 if(this.collapsed){
39596 return this.collapsedEl.getBox();
39598 var box = this.el.getBox();
39600 var sw = this.split.el.getWidth();
39607 updateBox : function(box){
39608 if(this.split && !this.collapsed){
39609 var sw = this.split.el.getWidth();
39611 this.split.el.setLeft(box.x);
39612 this.split.el.setTop(box.y);
39613 this.split.el.setHeight(box.height);
39616 if(this.collapsed){
39617 this.updateBody(null, box.height);
39619 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39623 Roo.bootstrap.layout.West = function(config){
39624 config.region = "west";
39625 config.cursor = "w-resize";
39627 Roo.bootstrap.layout.Split.call(this, config);
39629 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39630 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39631 this.split.el.addClass("roo-layout-split-h");
39635 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39636 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39638 onRender: function(ctr, pos)
39640 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39641 var size = this.config.initialSize || this.config.width;
39642 if(typeof size != "undefined"){
39643 this.el.setWidth(size);
39647 getBox : function(){
39648 if(this.collapsed){
39649 return this.collapsedEl.getBox();
39651 var box = this.el.getBox();
39652 if (box.width == 0) {
39653 box.width = this.config.width; // kludge?
39656 box.width += this.split.el.getWidth();
39661 updateBox : function(box){
39662 if(this.split && !this.collapsed){
39663 var sw = this.split.el.getWidth();
39665 this.split.el.setLeft(box.x+box.width);
39666 this.split.el.setTop(box.y);
39667 this.split.el.setHeight(box.height);
39669 if(this.collapsed){
39670 this.updateBody(null, box.height);
39672 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39674 });Roo.namespace("Roo.bootstrap.panel");/*
39676 * Ext JS Library 1.1.1
39677 * Copyright(c) 2006-2007, Ext JS, LLC.
39679 * Originally Released Under LGPL - original licence link has changed is not relivant.
39682 * <script type="text/javascript">
39685 * @class Roo.ContentPanel
39686 * @extends Roo.util.Observable
39687 * A basic ContentPanel element.
39688 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
39689 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
39690 * @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
39691 * @cfg {Boolean} closable True if the panel can be closed/removed
39692 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
39693 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39694 * @cfg {Toolbar} toolbar A toolbar for this panel
39695 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
39696 * @cfg {String} title The title for this panel
39697 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39698 * @cfg {String} url Calls {@link #setUrl} with this value
39699 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39700 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
39701 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
39702 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
39703 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
39704 * @cfg {Boolean} badges render the badges
39705 * @cfg {String} cls extra classes to use
39706 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39709 * Create a new ContentPanel.
39710 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39711 * @param {String/Object} config A string to set only the title or a config object
39712 * @param {String} content (optional) Set the HTML content for this panel
39713 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39715 Roo.bootstrap.panel.Content = function( config){
39717 this.tpl = config.tpl || false;
39719 var el = config.el;
39720 var content = config.content;
39722 if(config.autoCreate){ // xtype is available if this is called from factory
39725 this.el = Roo.get(el);
39726 if(!this.el && config && config.autoCreate){
39727 if(typeof config.autoCreate == "object"){
39728 if(!config.autoCreate.id){
39729 config.autoCreate.id = config.id||el;
39731 this.el = Roo.DomHelper.append(document.body,
39732 config.autoCreate, true);
39736 cls: (config.cls || '') +
39737 (config.background ? ' bg-' + config.background : '') +
39738 " roo-layout-inactive-content",
39741 if (config.iframe) {
39745 style : 'border: 0px',
39746 src : 'about:blank'
39752 elcfg.html = config.html;
39756 this.el = Roo.DomHelper.append(document.body, elcfg , true);
39757 if (config.iframe) {
39758 this.iframeEl = this.el.select('iframe',true).first();
39763 this.closable = false;
39764 this.loaded = false;
39765 this.active = false;
39768 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39770 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39772 this.wrapEl = this.el; //this.el.wrap();
39774 if (config.toolbar.items) {
39775 ti = config.toolbar.items ;
39776 delete config.toolbar.items ;
39780 this.toolbar.render(this.wrapEl, 'before');
39781 for(var i =0;i < ti.length;i++) {
39782 // Roo.log(['add child', items[i]]);
39783 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39785 this.toolbar.items = nitems;
39786 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39787 delete config.toolbar;
39791 // xtype created footer. - not sure if will work as we normally have to render first..
39792 if (this.footer && !this.footer.el && this.footer.xtype) {
39793 if (!this.wrapEl) {
39794 this.wrapEl = this.el.wrap();
39797 this.footer.container = this.wrapEl.createChild();
39799 this.footer = Roo.factory(this.footer, Roo);
39804 if(typeof config == "string"){
39805 this.title = config;
39807 Roo.apply(this, config);
39811 this.resizeEl = Roo.get(this.resizeEl, true);
39813 this.resizeEl = this.el;
39815 // handle view.xtype
39823 * Fires when this panel is activated.
39824 * @param {Roo.ContentPanel} this
39828 * @event deactivate
39829 * Fires when this panel is activated.
39830 * @param {Roo.ContentPanel} this
39832 "deactivate" : true,
39836 * Fires when this panel is resized if fitToFrame is true.
39837 * @param {Roo.ContentPanel} this
39838 * @param {Number} width The width after any component adjustments
39839 * @param {Number} height The height after any component adjustments
39845 * Fires when this tab is created
39846 * @param {Roo.ContentPanel} this
39857 if(this.autoScroll && !this.iframe){
39858 this.resizeEl.setStyle("overflow", "auto");
39860 // fix randome scrolling
39861 //this.el.on('scroll', function() {
39862 // Roo.log('fix random scolling');
39863 // this.scrollTo('top',0);
39866 content = content || this.content;
39868 this.setContent(content);
39870 if(config && config.url){
39871 this.setUrl(this.url, this.params, this.loadOnce);
39876 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39878 if (this.view && typeof(this.view.xtype) != 'undefined') {
39879 this.view.el = this.el.appendChild(document.createElement("div"));
39880 this.view = Roo.factory(this.view);
39881 this.view.render && this.view.render(false, '');
39885 this.fireEvent('render', this);
39888 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39898 setRegion : function(region){
39899 this.region = region;
39900 this.setActiveClass(region && !this.background);
39904 setActiveClass: function(state)
39907 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39908 this.el.setStyle('position','relative');
39910 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39911 this.el.setStyle('position', 'absolute');
39916 * Returns the toolbar for this Panel if one was configured.
39917 * @return {Roo.Toolbar}
39919 getToolbar : function(){
39920 return this.toolbar;
39923 setActiveState : function(active)
39925 this.active = active;
39926 this.setActiveClass(active);
39928 if(this.fireEvent("deactivate", this) === false){
39933 this.fireEvent("activate", this);
39937 * Updates this panel's element (not for iframe)
39938 * @param {String} content The new content
39939 * @param {Boolean} loadScripts (optional) true to look for and process scripts
39941 setContent : function(content, loadScripts){
39946 this.el.update(content, loadScripts);
39949 ignoreResize : function(w, h){
39950 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39953 this.lastSize = {width: w, height: h};
39958 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39959 * @return {Roo.UpdateManager} The UpdateManager
39961 getUpdateManager : function(){
39965 return this.el.getUpdateManager();
39968 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39969 * Does not work with IFRAME contents
39970 * @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:
39973 url: "your-url.php",
39974 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39975 callback: yourFunction,
39976 scope: yourObject, //(optional scope)
39979 text: "Loading...",
39985 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39986 * 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.
39987 * @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}
39988 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39989 * @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.
39990 * @return {Roo.ContentPanel} this
39998 var um = this.el.getUpdateManager();
39999 um.update.apply(um, arguments);
40005 * 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.
40006 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40007 * @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)
40008 * @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)
40009 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40011 setUrl : function(url, params, loadOnce){
40013 this.iframeEl.dom.src = url;
40017 if(this.refreshDelegate){
40018 this.removeListener("activate", this.refreshDelegate);
40020 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40021 this.on("activate", this.refreshDelegate);
40022 return this.el.getUpdateManager();
40025 _handleRefresh : function(url, params, loadOnce){
40026 if(!loadOnce || !this.loaded){
40027 var updater = this.el.getUpdateManager();
40028 updater.update(url, params, this._setLoaded.createDelegate(this));
40032 _setLoaded : function(){
40033 this.loaded = true;
40037 * Returns this panel's id
40040 getId : function(){
40045 * Returns this panel's element - used by regiosn to add.
40046 * @return {Roo.Element}
40048 getEl : function(){
40049 return this.wrapEl || this.el;
40054 adjustForComponents : function(width, height)
40056 //Roo.log('adjustForComponents ');
40057 if(this.resizeEl != this.el){
40058 width -= this.el.getFrameWidth('lr');
40059 height -= this.el.getFrameWidth('tb');
40062 var te = this.toolbar.getEl();
40063 te.setWidth(width);
40064 height -= te.getHeight();
40067 var te = this.footer.getEl();
40068 te.setWidth(width);
40069 height -= te.getHeight();
40073 if(this.adjustments){
40074 width += this.adjustments[0];
40075 height += this.adjustments[1];
40077 return {"width": width, "height": height};
40080 setSize : function(width, height){
40081 if(this.fitToFrame && !this.ignoreResize(width, height)){
40082 if(this.fitContainer && this.resizeEl != this.el){
40083 this.el.setSize(width, height);
40085 var size = this.adjustForComponents(width, height);
40087 this.iframeEl.setSize(width,height);
40090 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40091 this.fireEvent('resize', this, size.width, size.height);
40098 * Returns this panel's title
40101 getTitle : function(){
40103 if (typeof(this.title) != 'object') {
40108 for (var k in this.title) {
40109 if (!this.title.hasOwnProperty(k)) {
40113 if (k.indexOf('-') >= 0) {
40114 var s = k.split('-');
40115 for (var i = 0; i<s.length; i++) {
40116 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40119 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40126 * Set this panel's title
40127 * @param {String} title
40129 setTitle : function(title){
40130 this.title = title;
40132 this.region.updatePanelTitle(this, title);
40137 * Returns true is this panel was configured to be closable
40138 * @return {Boolean}
40140 isClosable : function(){
40141 return this.closable;
40144 beforeSlide : function(){
40146 this.resizeEl.clip();
40149 afterSlide : function(){
40151 this.resizeEl.unclip();
40155 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40156 * Will fail silently if the {@link #setUrl} method has not been called.
40157 * This does not activate the panel, just updates its content.
40159 refresh : function(){
40160 if(this.refreshDelegate){
40161 this.loaded = false;
40162 this.refreshDelegate();
40167 * Destroys this panel
40169 destroy : function(){
40170 this.el.removeAllListeners();
40171 var tempEl = document.createElement("span");
40172 tempEl.appendChild(this.el.dom);
40173 tempEl.innerHTML = "";
40179 * form - if the content panel contains a form - this is a reference to it.
40180 * @type {Roo.form.Form}
40184 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40185 * This contains a reference to it.
40191 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40201 * @param {Object} cfg Xtype definition of item to add.
40205 getChildContainer: function () {
40206 return this.getEl();
40211 var ret = new Roo.factory(cfg);
40216 if (cfg.xtype.match(/^Form$/)) {
40219 //if (this.footer) {
40220 // el = this.footer.container.insertSibling(false, 'before');
40222 el = this.el.createChild();
40225 this.form = new Roo.form.Form(cfg);
40228 if ( this.form.allItems.length) {
40229 this.form.render(el.dom);
40233 // should only have one of theses..
40234 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40235 // views.. should not be just added - used named prop 'view''
40237 cfg.el = this.el.appendChild(document.createElement("div"));
40240 var ret = new Roo.factory(cfg);
40242 ret.render && ret.render(false, ''); // render blank..
40252 * @class Roo.bootstrap.panel.Grid
40253 * @extends Roo.bootstrap.panel.Content
40255 * Create a new GridPanel.
40256 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40257 * @param {Object} config A the config object
40263 Roo.bootstrap.panel.Grid = function(config)
40267 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40268 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40270 config.el = this.wrapper;
40271 //this.el = this.wrapper;
40273 if (config.container) {
40274 // ctor'ed from a Border/panel.grid
40277 this.wrapper.setStyle("overflow", "hidden");
40278 this.wrapper.addClass('roo-grid-container');
40283 if(config.toolbar){
40284 var tool_el = this.wrapper.createChild();
40285 this.toolbar = Roo.factory(config.toolbar);
40287 if (config.toolbar.items) {
40288 ti = config.toolbar.items ;
40289 delete config.toolbar.items ;
40293 this.toolbar.render(tool_el);
40294 for(var i =0;i < ti.length;i++) {
40295 // Roo.log(['add child', items[i]]);
40296 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40298 this.toolbar.items = nitems;
40300 delete config.toolbar;
40303 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40304 config.grid.scrollBody = true;;
40305 config.grid.monitorWindowResize = false; // turn off autosizing
40306 config.grid.autoHeight = false;
40307 config.grid.autoWidth = false;
40309 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40311 if (config.background) {
40312 // render grid on panel activation (if panel background)
40313 this.on('activate', function(gp) {
40314 if (!gp.grid.rendered) {
40315 gp.grid.render(this.wrapper);
40316 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40321 this.grid.render(this.wrapper);
40322 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40325 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40326 // ??? needed ??? config.el = this.wrapper;
40331 // xtype created footer. - not sure if will work as we normally have to render first..
40332 if (this.footer && !this.footer.el && this.footer.xtype) {
40334 var ctr = this.grid.getView().getFooterPanel(true);
40335 this.footer.dataSource = this.grid.dataSource;
40336 this.footer = Roo.factory(this.footer, Roo);
40337 this.footer.render(ctr);
40347 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40348 getId : function(){
40349 return this.grid.id;
40353 * Returns the grid for this panel
40354 * @return {Roo.bootstrap.Table}
40356 getGrid : function(){
40360 setSize : function(width, height){
40361 if(!this.ignoreResize(width, height)){
40362 var grid = this.grid;
40363 var size = this.adjustForComponents(width, height);
40364 // tfoot is not a footer?
40367 var gridel = grid.getGridEl();
40368 gridel.setSize(size.width, size.height);
40370 var tbd = grid.getGridEl().select('tbody', true).first();
40371 var thd = grid.getGridEl().select('thead',true).first();
40372 var tbf= grid.getGridEl().select('tfoot', true).first();
40375 size.height -= tbf.getHeight();
40378 size.height -= thd.getHeight();
40381 tbd.setSize(size.width, size.height );
40382 // this is for the account management tab -seems to work there.
40383 var thd = grid.getGridEl().select('thead',true).first();
40385 // tbd.setSize(size.width, size.height - thd.getHeight());
40394 beforeSlide : function(){
40395 this.grid.getView().scroller.clip();
40398 afterSlide : function(){
40399 this.grid.getView().scroller.unclip();
40402 destroy : function(){
40403 this.grid.destroy();
40405 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40410 * @class Roo.bootstrap.panel.Nest
40411 * @extends Roo.bootstrap.panel.Content
40413 * Create a new Panel, that can contain a layout.Border.
40416 * @param {Roo.BorderLayout} layout The layout for this panel
40417 * @param {String/Object} config A string to set only the title or a config object
40419 Roo.bootstrap.panel.Nest = function(config)
40421 // construct with only one argument..
40422 /* FIXME - implement nicer consturctors
40423 if (layout.layout) {
40425 layout = config.layout;
40426 delete config.layout;
40428 if (layout.xtype && !layout.getEl) {
40429 // then layout needs constructing..
40430 layout = Roo.factory(layout, Roo);
40434 config.el = config.layout.getEl();
40436 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40438 config.layout.monitorWindowResize = false; // turn off autosizing
40439 this.layout = config.layout;
40440 this.layout.getEl().addClass("roo-layout-nested-layout");
40441 this.layout.parent = this;
40448 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40450 setSize : function(width, height){
40451 if(!this.ignoreResize(width, height)){
40452 var size = this.adjustForComponents(width, height);
40453 var el = this.layout.getEl();
40454 if (size.height < 1) {
40455 el.setWidth(size.width);
40457 el.setSize(size.width, size.height);
40459 var touch = el.dom.offsetWidth;
40460 this.layout.layout();
40461 // ie requires a double layout on the first pass
40462 if(Roo.isIE && !this.initialized){
40463 this.initialized = true;
40464 this.layout.layout();
40469 // activate all subpanels if not currently active..
40471 setActiveState : function(active){
40472 this.active = active;
40473 this.setActiveClass(active);
40476 this.fireEvent("deactivate", this);
40480 this.fireEvent("activate", this);
40481 // not sure if this should happen before or after..
40482 if (!this.layout) {
40483 return; // should not happen..
40486 for (var r in this.layout.regions) {
40487 reg = this.layout.getRegion(r);
40488 if (reg.getActivePanel()) {
40489 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40490 reg.setActivePanel(reg.getActivePanel());
40493 if (!reg.panels.length) {
40496 reg.showPanel(reg.getPanel(0));
40505 * Returns the nested BorderLayout for this panel
40506 * @return {Roo.BorderLayout}
40508 getLayout : function(){
40509 return this.layout;
40513 * Adds a xtype elements to the layout of the nested panel
40517 xtype : 'ContentPanel',
40524 xtype : 'NestedLayoutPanel',
40530 items : [ ... list of content panels or nested layout panels.. ]
40534 * @param {Object} cfg Xtype definition of item to add.
40536 addxtype : function(cfg) {
40537 return this.layout.addxtype(cfg);
40542 * Ext JS Library 1.1.1
40543 * Copyright(c) 2006-2007, Ext JS, LLC.
40545 * Originally Released Under LGPL - original licence link has changed is not relivant.
40548 * <script type="text/javascript">
40551 * @class Roo.TabPanel
40552 * @extends Roo.util.Observable
40553 * A lightweight tab container.
40557 // basic tabs 1, built from existing content
40558 var tabs = new Roo.TabPanel("tabs1");
40559 tabs.addTab("script", "View Script");
40560 tabs.addTab("markup", "View Markup");
40561 tabs.activate("script");
40563 // more advanced tabs, built from javascript
40564 var jtabs = new Roo.TabPanel("jtabs");
40565 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40567 // set up the UpdateManager
40568 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40569 var updater = tab2.getUpdateManager();
40570 updater.setDefaultUrl("ajax1.htm");
40571 tab2.on('activate', updater.refresh, updater, true);
40573 // Use setUrl for Ajax loading
40574 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40575 tab3.setUrl("ajax2.htm", null, true);
40578 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40581 jtabs.activate("jtabs-1");
40584 * Create a new TabPanel.
40585 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40586 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40588 Roo.bootstrap.panel.Tabs = function(config){
40590 * The container element for this TabPanel.
40591 * @type Roo.Element
40593 this.el = Roo.get(config.el);
40596 if(typeof config == "boolean"){
40597 this.tabPosition = config ? "bottom" : "top";
40599 Roo.apply(this, config);
40603 if(this.tabPosition == "bottom"){
40604 // if tabs are at the bottom = create the body first.
40605 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40606 this.el.addClass("roo-tabs-bottom");
40608 // next create the tabs holders
40610 if (this.tabPosition == "west"){
40612 var reg = this.region; // fake it..
40614 if (!reg.mgr.parent) {
40617 reg = reg.mgr.parent.region;
40619 Roo.log("got nest?");
40621 if (reg.mgr.getRegion('west')) {
40622 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40623 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40624 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40625 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40626 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40634 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40635 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40636 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40637 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40642 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40645 // finally - if tabs are at the top, then create the body last..
40646 if(this.tabPosition != "bottom"){
40647 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40648 * @type Roo.Element
40650 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40651 this.el.addClass("roo-tabs-top");
40655 this.bodyEl.setStyle("position", "relative");
40657 this.active = null;
40658 this.activateDelegate = this.activate.createDelegate(this);
40663 * Fires when the active tab changes
40664 * @param {Roo.TabPanel} this
40665 * @param {Roo.TabPanelItem} activePanel The new active tab
40669 * @event beforetabchange
40670 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40671 * @param {Roo.TabPanel} this
40672 * @param {Object} e Set cancel to true on this object to cancel the tab change
40673 * @param {Roo.TabPanelItem} tab The tab being changed to
40675 "beforetabchange" : true
40678 Roo.EventManager.onWindowResize(this.onResize, this);
40679 this.cpad = this.el.getPadding("lr");
40680 this.hiddenCount = 0;
40683 // toolbar on the tabbar support...
40684 if (this.toolbar) {
40685 alert("no toolbar support yet");
40686 this.toolbar = false;
40688 var tcfg = this.toolbar;
40689 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
40690 this.toolbar = new Roo.Toolbar(tcfg);
40691 if (Roo.isSafari) {
40692 var tbl = tcfg.container.child('table', true);
40693 tbl.setAttribute('width', '100%');
40701 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40704 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40706 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40708 tabPosition : "top",
40710 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40712 currentTabWidth : 0,
40714 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40718 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40722 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40724 preferredTabWidth : 175,
40726 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40728 resizeTabs : false,
40730 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40732 monitorResize : true,
40734 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
40736 toolbar : false, // set by caller..
40738 region : false, /// set by caller
40740 disableTooltips : true, // not used yet...
40743 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40744 * @param {String} id The id of the div to use <b>or create</b>
40745 * @param {String} text The text for the tab
40746 * @param {String} content (optional) Content to put in the TabPanelItem body
40747 * @param {Boolean} closable (optional) True to create a close icon on the tab
40748 * @return {Roo.TabPanelItem} The created TabPanelItem
40750 addTab : function(id, text, content, closable, tpl)
40752 var item = new Roo.bootstrap.panel.TabItem({
40756 closable : closable,
40759 this.addTabItem(item);
40761 item.setContent(content);
40767 * Returns the {@link Roo.TabPanelItem} with the specified id/index
40768 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40769 * @return {Roo.TabPanelItem}
40771 getTab : function(id){
40772 return this.items[id];
40776 * Hides the {@link Roo.TabPanelItem} with the specified id/index
40777 * @param {String/Number} id The id or index of the TabPanelItem to hide.
40779 hideTab : function(id){
40780 var t = this.items[id];
40783 this.hiddenCount++;
40784 this.autoSizeTabs();
40789 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40790 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40792 unhideTab : function(id){
40793 var t = this.items[id];
40795 t.setHidden(false);
40796 this.hiddenCount--;
40797 this.autoSizeTabs();
40802 * Adds an existing {@link Roo.TabPanelItem}.
40803 * @param {Roo.TabPanelItem} item The TabPanelItem to add
40805 addTabItem : function(item)
40807 this.items[item.id] = item;
40808 this.items.push(item);
40809 this.autoSizeTabs();
40810 // if(this.resizeTabs){
40811 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40812 // this.autoSizeTabs();
40814 // item.autoSize();
40819 * Removes a {@link Roo.TabPanelItem}.
40820 * @param {String/Number} id The id or index of the TabPanelItem to remove.
40822 removeTab : function(id){
40823 var items = this.items;
40824 var tab = items[id];
40825 if(!tab) { return; }
40826 var index = items.indexOf(tab);
40827 if(this.active == tab && items.length > 1){
40828 var newTab = this.getNextAvailable(index);
40833 this.stripEl.dom.removeChild(tab.pnode.dom);
40834 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40835 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40837 items.splice(index, 1);
40838 delete this.items[tab.id];
40839 tab.fireEvent("close", tab);
40840 tab.purgeListeners();
40841 this.autoSizeTabs();
40844 getNextAvailable : function(start){
40845 var items = this.items;
40847 // look for a next tab that will slide over to
40848 // replace the one being removed
40849 while(index < items.length){
40850 var item = items[++index];
40851 if(item && !item.isHidden()){
40855 // if one isn't found select the previous tab (on the left)
40858 var item = items[--index];
40859 if(item && !item.isHidden()){
40867 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40868 * @param {String/Number} id The id or index of the TabPanelItem to disable.
40870 disableTab : function(id){
40871 var tab = this.items[id];
40872 if(tab && this.active != tab){
40878 * Enables a {@link Roo.TabPanelItem} that is disabled.
40879 * @param {String/Number} id The id or index of the TabPanelItem to enable.
40881 enableTab : function(id){
40882 var tab = this.items[id];
40887 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40888 * @param {String/Number} id The id or index of the TabPanelItem to activate.
40889 * @return {Roo.TabPanelItem} The TabPanelItem.
40891 activate : function(id)
40893 //Roo.log('activite:' + id);
40895 var tab = this.items[id];
40899 if(tab == this.active || tab.disabled){
40903 this.fireEvent("beforetabchange", this, e, tab);
40904 if(e.cancel !== true && !tab.disabled){
40906 this.active.hide();
40908 this.active = this.items[id];
40909 this.active.show();
40910 this.fireEvent("tabchange", this, this.active);
40916 * Gets the active {@link Roo.TabPanelItem}.
40917 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40919 getActiveTab : function(){
40920 return this.active;
40924 * Updates the tab body element to fit the height of the container element
40925 * for overflow scrolling
40926 * @param {Number} targetHeight (optional) Override the starting height from the elements height
40928 syncHeight : function(targetHeight){
40929 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40930 var bm = this.bodyEl.getMargins();
40931 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40932 this.bodyEl.setHeight(newHeight);
40936 onResize : function(){
40937 if(this.monitorResize){
40938 this.autoSizeTabs();
40943 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40945 beginUpdate : function(){
40946 this.updating = true;
40950 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40952 endUpdate : function(){
40953 this.updating = false;
40954 this.autoSizeTabs();
40958 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40960 autoSizeTabs : function()
40962 var count = this.items.length;
40963 var vcount = count - this.hiddenCount;
40966 this.stripEl.hide();
40968 this.stripEl.show();
40971 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40976 var w = Math.max(this.el.getWidth() - this.cpad, 10);
40977 var availWidth = Math.floor(w / vcount);
40978 var b = this.stripBody;
40979 if(b.getWidth() > w){
40980 var tabs = this.items;
40981 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40982 if(availWidth < this.minTabWidth){
40983 /*if(!this.sleft){ // incomplete scrolling code
40984 this.createScrollButtons();
40987 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40990 if(this.currentTabWidth < this.preferredTabWidth){
40991 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40997 * Returns the number of tabs in this TabPanel.
41000 getCount : function(){
41001 return this.items.length;
41005 * Resizes all the tabs to the passed width
41006 * @param {Number} The new width
41008 setTabWidth : function(width){
41009 this.currentTabWidth = width;
41010 for(var i = 0, len = this.items.length; i < len; i++) {
41011 if(!this.items[i].isHidden()) {
41012 this.items[i].setWidth(width);
41018 * Destroys this TabPanel
41019 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41021 destroy : function(removeEl){
41022 Roo.EventManager.removeResizeListener(this.onResize, this);
41023 for(var i = 0, len = this.items.length; i < len; i++){
41024 this.items[i].purgeListeners();
41026 if(removeEl === true){
41027 this.el.update("");
41032 createStrip : function(container)
41034 var strip = document.createElement("nav");
41035 strip.className = Roo.bootstrap.version == 4 ?
41036 "navbar-light bg-light" :
41037 "navbar navbar-default"; //"x-tabs-wrap";
41038 container.appendChild(strip);
41042 createStripList : function(strip)
41044 // div wrapper for retard IE
41045 // returns the "tr" element.
41046 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41047 //'<div class="x-tabs-strip-wrap">'+
41048 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41049 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41050 return strip.firstChild; //.firstChild.firstChild.firstChild;
41052 createBody : function(container)
41054 var body = document.createElement("div");
41055 Roo.id(body, "tab-body");
41056 //Roo.fly(body).addClass("x-tabs-body");
41057 Roo.fly(body).addClass("tab-content");
41058 container.appendChild(body);
41061 createItemBody :function(bodyEl, id){
41062 var body = Roo.getDom(id);
41064 body = document.createElement("div");
41067 //Roo.fly(body).addClass("x-tabs-item-body");
41068 Roo.fly(body).addClass("tab-pane");
41069 bodyEl.insertBefore(body, bodyEl.firstChild);
41073 createStripElements : function(stripEl, text, closable, tpl)
41075 var td = document.createElement("li"); // was td..
41076 td.className = 'nav-item';
41078 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41081 stripEl.appendChild(td);
41083 td.className = "x-tabs-closable";
41084 if(!this.closeTpl){
41085 this.closeTpl = new Roo.Template(
41086 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41087 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41088 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41091 var el = this.closeTpl.overwrite(td, {"text": text});
41092 var close = el.getElementsByTagName("div")[0];
41093 var inner = el.getElementsByTagName("em")[0];
41094 return {"el": el, "close": close, "inner": inner};
41097 // not sure what this is..
41098 // if(!this.tabTpl){
41099 //this.tabTpl = new Roo.Template(
41100 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41101 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41103 // this.tabTpl = new Roo.Template(
41104 // '<a href="#">' +
41105 // '<span unselectable="on"' +
41106 // (this.disableTooltips ? '' : ' title="{text}"') +
41107 // ' >{text}</span></a>'
41113 var template = tpl || this.tabTpl || false;
41116 template = new Roo.Template(
41117 Roo.bootstrap.version == 4 ?
41119 '<a class="nav-link" href="#" unselectable="on"' +
41120 (this.disableTooltips ? '' : ' title="{text}"') +
41123 '<a class="nav-link" href="#">' +
41124 '<span unselectable="on"' +
41125 (this.disableTooltips ? '' : ' title="{text}"') +
41126 ' >{text}</span></a>'
41131 switch (typeof(template)) {
41135 template = new Roo.Template(template);
41141 var el = template.overwrite(td, {"text": text});
41143 var inner = el.getElementsByTagName("span")[0];
41145 return {"el": el, "inner": inner};
41153 * @class Roo.TabPanelItem
41154 * @extends Roo.util.Observable
41155 * Represents an individual item (tab plus body) in a TabPanel.
41156 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41157 * @param {String} id The id of this TabPanelItem
41158 * @param {String} text The text for the tab of this TabPanelItem
41159 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41161 Roo.bootstrap.panel.TabItem = function(config){
41163 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41164 * @type Roo.TabPanel
41166 this.tabPanel = config.panel;
41168 * The id for this TabPanelItem
41171 this.id = config.id;
41173 this.disabled = false;
41175 this.text = config.text;
41177 this.loaded = false;
41178 this.closable = config.closable;
41181 * The body element for this TabPanelItem.
41182 * @type Roo.Element
41184 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41185 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41186 this.bodyEl.setStyle("display", "block");
41187 this.bodyEl.setStyle("zoom", "1");
41188 //this.hideAction();
41190 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41192 this.el = Roo.get(els.el);
41193 this.inner = Roo.get(els.inner, true);
41194 this.textEl = Roo.bootstrap.version == 4 ?
41195 this.el : Roo.get(this.el.dom.firstChild, true);
41197 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41198 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41201 // this.el.on("mousedown", this.onTabMouseDown, this);
41202 this.el.on("click", this.onTabClick, this);
41204 if(config.closable){
41205 var c = Roo.get(els.close, true);
41206 c.dom.title = this.closeText;
41207 c.addClassOnOver("close-over");
41208 c.on("click", this.closeClick, this);
41214 * Fires when this tab becomes the active tab.
41215 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41216 * @param {Roo.TabPanelItem} this
41220 * @event beforeclose
41221 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41222 * @param {Roo.TabPanelItem} this
41223 * @param {Object} e Set cancel to true on this object to cancel the close.
41225 "beforeclose": true,
41228 * Fires when this tab is closed.
41229 * @param {Roo.TabPanelItem} this
41233 * @event deactivate
41234 * Fires when this tab is no longer the active tab.
41235 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41236 * @param {Roo.TabPanelItem} this
41238 "deactivate" : true
41240 this.hidden = false;
41242 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41245 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41247 purgeListeners : function(){
41248 Roo.util.Observable.prototype.purgeListeners.call(this);
41249 this.el.removeAllListeners();
41252 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41255 this.status_node.addClass("active");
41258 this.tabPanel.stripWrap.repaint();
41260 this.fireEvent("activate", this.tabPanel, this);
41264 * Returns true if this tab is the active tab.
41265 * @return {Boolean}
41267 isActive : function(){
41268 return this.tabPanel.getActiveTab() == this;
41272 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41275 this.status_node.removeClass("active");
41277 this.fireEvent("deactivate", this.tabPanel, this);
41280 hideAction : function(){
41281 this.bodyEl.hide();
41282 this.bodyEl.setStyle("position", "absolute");
41283 this.bodyEl.setLeft("-20000px");
41284 this.bodyEl.setTop("-20000px");
41287 showAction : function(){
41288 this.bodyEl.setStyle("position", "relative");
41289 this.bodyEl.setTop("");
41290 this.bodyEl.setLeft("");
41291 this.bodyEl.show();
41295 * Set the tooltip for the tab.
41296 * @param {String} tooltip The tab's tooltip
41298 setTooltip : function(text){
41299 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41300 this.textEl.dom.qtip = text;
41301 this.textEl.dom.removeAttribute('title');
41303 this.textEl.dom.title = text;
41307 onTabClick : function(e){
41308 e.preventDefault();
41309 this.tabPanel.activate(this.id);
41312 onTabMouseDown : function(e){
41313 e.preventDefault();
41314 this.tabPanel.activate(this.id);
41317 getWidth : function(){
41318 return this.inner.getWidth();
41321 setWidth : function(width){
41322 var iwidth = width - this.linode.getPadding("lr");
41323 this.inner.setWidth(iwidth);
41324 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41325 this.linode.setWidth(width);
41329 * Show or hide the tab
41330 * @param {Boolean} hidden True to hide or false to show.
41332 setHidden : function(hidden){
41333 this.hidden = hidden;
41334 this.linode.setStyle("display", hidden ? "none" : "");
41338 * Returns true if this tab is "hidden"
41339 * @return {Boolean}
41341 isHidden : function(){
41342 return this.hidden;
41346 * Returns the text for this tab
41349 getText : function(){
41353 autoSize : function(){
41354 //this.el.beginMeasure();
41355 this.textEl.setWidth(1);
41357 * #2804 [new] Tabs in Roojs
41358 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41360 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41361 //this.el.endMeasure();
41365 * Sets the text for the tab (Note: this also sets the tooltip text)
41366 * @param {String} text The tab's text and tooltip
41368 setText : function(text){
41370 this.textEl.update(text);
41371 this.setTooltip(text);
41372 //if(!this.tabPanel.resizeTabs){
41373 // this.autoSize();
41377 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41379 activate : function(){
41380 this.tabPanel.activate(this.id);
41384 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41386 disable : function(){
41387 if(this.tabPanel.active != this){
41388 this.disabled = true;
41389 this.status_node.addClass("disabled");
41394 * Enables this TabPanelItem if it was previously disabled.
41396 enable : function(){
41397 this.disabled = false;
41398 this.status_node.removeClass("disabled");
41402 * Sets the content for this TabPanelItem.
41403 * @param {String} content The content
41404 * @param {Boolean} loadScripts true to look for and load scripts
41406 setContent : function(content, loadScripts){
41407 this.bodyEl.update(content, loadScripts);
41411 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41412 * @return {Roo.UpdateManager} The UpdateManager
41414 getUpdateManager : function(){
41415 return this.bodyEl.getUpdateManager();
41419 * Set a URL to be used to load the content for this TabPanelItem.
41420 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41421 * @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)
41422 * @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)
41423 * @return {Roo.UpdateManager} The UpdateManager
41425 setUrl : function(url, params, loadOnce){
41426 if(this.refreshDelegate){
41427 this.un('activate', this.refreshDelegate);
41429 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41430 this.on("activate", this.refreshDelegate);
41431 return this.bodyEl.getUpdateManager();
41435 _handleRefresh : function(url, params, loadOnce){
41436 if(!loadOnce || !this.loaded){
41437 var updater = this.bodyEl.getUpdateManager();
41438 updater.update(url, params, this._setLoaded.createDelegate(this));
41443 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41444 * Will fail silently if the setUrl method has not been called.
41445 * This does not activate the panel, just updates its content.
41447 refresh : function(){
41448 if(this.refreshDelegate){
41449 this.loaded = false;
41450 this.refreshDelegate();
41455 _setLoaded : function(){
41456 this.loaded = true;
41460 closeClick : function(e){
41463 this.fireEvent("beforeclose", this, o);
41464 if(o.cancel !== true){
41465 this.tabPanel.removeTab(this.id);
41469 * The text displayed in the tooltip for the close icon.
41472 closeText : "Close this tab"
41475 * This script refer to:
41476 * Title: International Telephone Input
41477 * Author: Jack O'Connor
41478 * Code version: v12.1.12
41479 * Availability: https://github.com/jackocnr/intl-tel-input.git
41482 Roo.bootstrap.PhoneInputData = function() {
41485 "Afghanistan (افغانستان)",
41490 "Albania (Shqipëri)",
41495 "Algeria (الجزائر)",
41520 "Antigua and Barbuda",
41530 "Armenia (Հայաստան)",
41546 "Austria (Österreich)",
41551 "Azerbaijan (Azərbaycan)",
41561 "Bahrain (البحرين)",
41566 "Bangladesh (বাংলাদেশ)",
41576 "Belarus (Беларусь)",
41581 "Belgium (België)",
41611 "Bosnia and Herzegovina (Босна и Херцеговина)",
41626 "British Indian Ocean Territory",
41631 "British Virgin Islands",
41641 "Bulgaria (България)",
41651 "Burundi (Uburundi)",
41656 "Cambodia (កម្ពុជា)",
41661 "Cameroon (Cameroun)",
41670 ["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"]
41673 "Cape Verde (Kabu Verdi)",
41678 "Caribbean Netherlands",
41689 "Central African Republic (République centrafricaine)",
41709 "Christmas Island",
41715 "Cocos (Keeling) Islands",
41726 "Comoros (جزر القمر)",
41731 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41736 "Congo (Republic) (Congo-Brazzaville)",
41756 "Croatia (Hrvatska)",
41777 "Czech Republic (Česká republika)",
41782 "Denmark (Danmark)",
41797 "Dominican Republic (República Dominicana)",
41801 ["809", "829", "849"]
41819 "Equatorial Guinea (Guinea Ecuatorial)",
41839 "Falkland Islands (Islas Malvinas)",
41844 "Faroe Islands (Føroyar)",
41865 "French Guiana (Guyane française)",
41870 "French Polynesia (Polynésie française)",
41885 "Georgia (საქართველო)",
41890 "Germany (Deutschland)",
41910 "Greenland (Kalaallit Nunaat)",
41947 "Guinea-Bissau (Guiné Bissau)",
41972 "Hungary (Magyarország)",
41977 "Iceland (Ísland)",
41997 "Iraq (العراق)",
42013 "Israel (ישראל)",
42040 "Jordan (الأردن)",
42045 "Kazakhstan (Казахстан)",
42066 "Kuwait (الكويت)",
42071 "Kyrgyzstan (Кыргызстан)",
42081 "Latvia (Latvija)",
42086 "Lebanon (لبنان)",
42101 "Libya (ليبيا)",
42111 "Lithuania (Lietuva)",
42126 "Macedonia (FYROM) (Македонија)",
42131 "Madagascar (Madagasikara)",
42161 "Marshall Islands",
42171 "Mauritania (موريتانيا)",
42176 "Mauritius (Moris)",
42197 "Moldova (Republica Moldova)",
42207 "Mongolia (Монгол)",
42212 "Montenegro (Crna Gora)",
42222 "Morocco (المغرب)",
42228 "Mozambique (Moçambique)",
42233 "Myanmar (Burma) (မြန်မာ)",
42238 "Namibia (Namibië)",
42253 "Netherlands (Nederland)",
42258 "New Caledonia (Nouvelle-Calédonie)",
42293 "North Korea (조선 민주주의 인민 공화국)",
42298 "Northern Mariana Islands",
42314 "Pakistan (پاکستان)",
42324 "Palestine (فلسطين)",
42334 "Papua New Guinea",
42376 "Réunion (La Réunion)",
42382 "Romania (România)",
42398 "Saint Barthélemy",
42409 "Saint Kitts and Nevis",
42419 "Saint Martin (Saint-Martin (partie française))",
42425 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42430 "Saint Vincent and the Grenadines",
42445 "São Tomé and Príncipe (São Tomé e Príncipe)",
42450 "Saudi Arabia (المملكة العربية السعودية)",
42455 "Senegal (Sénégal)",
42485 "Slovakia (Slovensko)",
42490 "Slovenia (Slovenija)",
42500 "Somalia (Soomaaliya)",
42510 "South Korea (대한민국)",
42515 "South Sudan (جنوب السودان)",
42525 "Sri Lanka (ශ්රී ලංකාව)",
42530 "Sudan (السودان)",
42540 "Svalbard and Jan Mayen",
42551 "Sweden (Sverige)",
42556 "Switzerland (Schweiz)",
42561 "Syria (سوريا)",
42606 "Trinidad and Tobago",
42611 "Tunisia (تونس)",
42616 "Turkey (Türkiye)",
42626 "Turks and Caicos Islands",
42636 "U.S. Virgin Islands",
42646 "Ukraine (Україна)",
42651 "United Arab Emirates (الإمارات العربية المتحدة)",
42673 "Uzbekistan (Oʻzbekiston)",
42683 "Vatican City (Città del Vaticano)",
42694 "Vietnam (Việt Nam)",
42699 "Wallis and Futuna (Wallis-et-Futuna)",
42704 "Western Sahara (الصحراء الغربية)",
42710 "Yemen (اليمن)",
42734 * This script refer to:
42735 * Title: International Telephone Input
42736 * Author: Jack O'Connor
42737 * Code version: v12.1.12
42738 * Availability: https://github.com/jackocnr/intl-tel-input.git
42742 * @class Roo.bootstrap.PhoneInput
42743 * @extends Roo.bootstrap.TriggerField
42744 * An input with International dial-code selection
42746 * @cfg {String} defaultDialCode default '+852'
42747 * @cfg {Array} preferedCountries default []
42750 * Create a new PhoneInput.
42751 * @param {Object} config Configuration options
42754 Roo.bootstrap.PhoneInput = function(config) {
42755 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42758 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42760 listWidth: undefined,
42762 selectedClass: 'active',
42764 invalidClass : "has-warning",
42766 validClass: 'has-success',
42768 allowed: '0123456789',
42773 * @cfg {String} defaultDialCode The default dial code when initializing the input
42775 defaultDialCode: '+852',
42778 * @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
42780 preferedCountries: false,
42782 getAutoCreate : function()
42784 var data = Roo.bootstrap.PhoneInputData();
42785 var align = this.labelAlign || this.parentLabelAlign();
42788 this.allCountries = [];
42789 this.dialCodeMapping = [];
42791 for (var i = 0; i < data.length; i++) {
42793 this.allCountries[i] = {
42797 priority: c[3] || 0,
42798 areaCodes: c[4] || null
42800 this.dialCodeMapping[c[2]] = {
42803 priority: c[3] || 0,
42804 areaCodes: c[4] || null
42816 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42817 maxlength: this.max_length,
42818 cls : 'form-control tel-input',
42819 autocomplete: 'new-password'
42822 var hiddenInput = {
42825 cls: 'hidden-tel-input'
42829 hiddenInput.name = this.name;
42832 if (this.disabled) {
42833 input.disabled = true;
42836 var flag_container = {
42853 cls: this.hasFeedback ? 'has-feedback' : '',
42859 cls: 'dial-code-holder',
42866 cls: 'roo-select2-container input-group',
42873 if (this.fieldLabel.length) {
42876 tooltip: 'This field is required'
42882 cls: 'control-label',
42888 html: this.fieldLabel
42891 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42897 if(this.indicatorpos == 'right') {
42898 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42905 if(align == 'left') {
42913 if(this.labelWidth > 12){
42914 label.style = "width: " + this.labelWidth + 'px';
42916 if(this.labelWidth < 13 && this.labelmd == 0){
42917 this.labelmd = this.labelWidth;
42919 if(this.labellg > 0){
42920 label.cls += ' col-lg-' + this.labellg;
42921 input.cls += ' col-lg-' + (12 - this.labellg);
42923 if(this.labelmd > 0){
42924 label.cls += ' col-md-' + this.labelmd;
42925 container.cls += ' col-md-' + (12 - this.labelmd);
42927 if(this.labelsm > 0){
42928 label.cls += ' col-sm-' + this.labelsm;
42929 container.cls += ' col-sm-' + (12 - this.labelsm);
42931 if(this.labelxs > 0){
42932 label.cls += ' col-xs-' + this.labelxs;
42933 container.cls += ' col-xs-' + (12 - this.labelxs);
42943 var settings = this;
42945 ['xs','sm','md','lg'].map(function(size){
42946 if (settings[size]) {
42947 cfg.cls += ' col-' + size + '-' + settings[size];
42951 this.store = new Roo.data.Store({
42952 proxy : new Roo.data.MemoryProxy({}),
42953 reader : new Roo.data.JsonReader({
42964 'name' : 'dialCode',
42968 'name' : 'priority',
42972 'name' : 'areaCodes',
42979 if(!this.preferedCountries) {
42980 this.preferedCountries = [
42987 var p = this.preferedCountries.reverse();
42990 for (var i = 0; i < p.length; i++) {
42991 for (var j = 0; j < this.allCountries.length; j++) {
42992 if(this.allCountries[j].iso2 == p[i]) {
42993 var t = this.allCountries[j];
42994 this.allCountries.splice(j,1);
42995 this.allCountries.unshift(t);
43001 this.store.proxy.data = {
43003 data: this.allCountries
43009 initEvents : function()
43012 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43014 this.indicator = this.indicatorEl();
43015 this.flag = this.flagEl();
43016 this.dialCodeHolder = this.dialCodeHolderEl();
43018 this.trigger = this.el.select('div.flag-box',true).first();
43019 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43024 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43025 _this.list.setWidth(lw);
43028 this.list.on('mouseover', this.onViewOver, this);
43029 this.list.on('mousemove', this.onViewMove, this);
43030 this.inputEl().on("keyup", this.onKeyUp, this);
43031 this.inputEl().on("keypress", this.onKeyPress, this);
43033 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43035 this.view = new Roo.View(this.list, this.tpl, {
43036 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43039 this.view.on('click', this.onViewClick, this);
43040 this.setValue(this.defaultDialCode);
43043 onTriggerClick : function(e)
43045 Roo.log('trigger click');
43050 if(this.isExpanded()){
43052 this.hasFocus = false;
43054 this.store.load({});
43055 this.hasFocus = true;
43060 isExpanded : function()
43062 return this.list.isVisible();
43065 collapse : function()
43067 if(!this.isExpanded()){
43071 Roo.get(document).un('mousedown', this.collapseIf, this);
43072 Roo.get(document).un('mousewheel', this.collapseIf, this);
43073 this.fireEvent('collapse', this);
43077 expand : function()
43081 if(this.isExpanded() || !this.hasFocus){
43085 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43086 this.list.setWidth(lw);
43089 this.restrictHeight();
43091 Roo.get(document).on('mousedown', this.collapseIf, this);
43092 Roo.get(document).on('mousewheel', this.collapseIf, this);
43094 this.fireEvent('expand', this);
43097 restrictHeight : function()
43099 this.list.alignTo(this.inputEl(), this.listAlign);
43100 this.list.alignTo(this.inputEl(), this.listAlign);
43103 onViewOver : function(e, t)
43105 if(this.inKeyMode){
43108 var item = this.view.findItemFromChild(t);
43111 var index = this.view.indexOf(item);
43112 this.select(index, false);
43117 onViewClick : function(view, doFocus, el, e)
43119 var index = this.view.getSelectedIndexes()[0];
43121 var r = this.store.getAt(index);
43124 this.onSelect(r, index);
43126 if(doFocus !== false && !this.blockFocus){
43127 this.inputEl().focus();
43131 onViewMove : function(e, t)
43133 this.inKeyMode = false;
43136 select : function(index, scrollIntoView)
43138 this.selectedIndex = index;
43139 this.view.select(index);
43140 if(scrollIntoView !== false){
43141 var el = this.view.getNode(index);
43143 this.list.scrollChildIntoView(el, false);
43148 createList : function()
43150 this.list = Roo.get(document.body).createChild({
43152 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43153 style: 'display:none'
43156 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43159 collapseIf : function(e)
43161 var in_combo = e.within(this.el);
43162 var in_list = e.within(this.list);
43163 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43165 if (in_combo || in_list || is_list) {
43171 onSelect : function(record, index)
43173 if(this.fireEvent('beforeselect', this, record, index) !== false){
43175 this.setFlagClass(record.data.iso2);
43176 this.setDialCode(record.data.dialCode);
43177 this.hasFocus = false;
43179 this.fireEvent('select', this, record, index);
43183 flagEl : function()
43185 var flag = this.el.select('div.flag',true).first();
43192 dialCodeHolderEl : function()
43194 var d = this.el.select('input.dial-code-holder',true).first();
43201 setDialCode : function(v)
43203 this.dialCodeHolder.dom.value = '+'+v;
43206 setFlagClass : function(n)
43208 this.flag.dom.className = 'flag '+n;
43211 getValue : function()
43213 var v = this.inputEl().getValue();
43214 if(this.dialCodeHolder) {
43215 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43220 setValue : function(v)
43222 var d = this.getDialCode(v);
43224 //invalid dial code
43225 if(v.length == 0 || !d || d.length == 0) {
43227 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43228 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43234 this.setFlagClass(this.dialCodeMapping[d].iso2);
43235 this.setDialCode(d);
43236 this.inputEl().dom.value = v.replace('+'+d,'');
43237 this.hiddenEl().dom.value = this.getValue();
43242 getDialCode : function(v)
43246 if (v.length == 0) {
43247 return this.dialCodeHolder.dom.value;
43251 if (v.charAt(0) != "+") {
43254 var numericChars = "";
43255 for (var i = 1; i < v.length; i++) {
43256 var c = v.charAt(i);
43259 if (this.dialCodeMapping[numericChars]) {
43260 dialCode = v.substr(1, i);
43262 if (numericChars.length == 4) {
43272 this.setValue(this.defaultDialCode);
43276 hiddenEl : function()
43278 return this.el.select('input.hidden-tel-input',true).first();
43281 // after setting val
43282 onKeyUp : function(e){
43283 this.setValue(this.getValue());
43286 onKeyPress : function(e){
43287 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43294 * @class Roo.bootstrap.MoneyField
43295 * @extends Roo.bootstrap.ComboBox
43296 * Bootstrap MoneyField class
43299 * Create a new MoneyField.
43300 * @param {Object} config Configuration options
43303 Roo.bootstrap.MoneyField = function(config) {
43305 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43309 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43312 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43314 allowDecimals : true,
43316 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43318 decimalSeparator : ".",
43320 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43322 decimalPrecision : 0,
43324 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43326 allowNegative : true,
43328 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43332 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43334 minValue : Number.NEGATIVE_INFINITY,
43336 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43338 maxValue : Number.MAX_VALUE,
43340 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43342 minText : "The minimum value for this field is {0}",
43344 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43346 maxText : "The maximum value for this field is {0}",
43348 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43349 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43351 nanText : "{0} is not a valid number",
43353 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43357 * @cfg {String} defaults currency of the MoneyField
43358 * value should be in lkey
43360 defaultCurrency : false,
43362 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43364 thousandsDelimiter : false,
43366 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43377 getAutoCreate : function()
43379 var align = this.labelAlign || this.parentLabelAlign();
43391 cls : 'form-control roo-money-amount-input',
43392 autocomplete: 'new-password'
43395 var hiddenInput = {
43399 cls: 'hidden-number-input'
43402 if(this.max_length) {
43403 input.maxlength = this.max_length;
43407 hiddenInput.name = this.name;
43410 if (this.disabled) {
43411 input.disabled = true;
43414 var clg = 12 - this.inputlg;
43415 var cmd = 12 - this.inputmd;
43416 var csm = 12 - this.inputsm;
43417 var cxs = 12 - this.inputxs;
43421 cls : 'row roo-money-field',
43425 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43429 cls: 'roo-select2-container input-group',
43433 cls : 'form-control roo-money-currency-input',
43434 autocomplete: 'new-password',
43436 name : this.currencyName
43440 cls : 'input-group-addon',
43454 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43458 cls: this.hasFeedback ? 'has-feedback' : '',
43469 if (this.fieldLabel.length) {
43472 tooltip: 'This field is required'
43478 cls: 'control-label',
43484 html: this.fieldLabel
43487 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43493 if(this.indicatorpos == 'right') {
43494 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43501 if(align == 'left') {
43509 if(this.labelWidth > 12){
43510 label.style = "width: " + this.labelWidth + 'px';
43512 if(this.labelWidth < 13 && this.labelmd == 0){
43513 this.labelmd = this.labelWidth;
43515 if(this.labellg > 0){
43516 label.cls += ' col-lg-' + this.labellg;
43517 input.cls += ' col-lg-' + (12 - this.labellg);
43519 if(this.labelmd > 0){
43520 label.cls += ' col-md-' + this.labelmd;
43521 container.cls += ' col-md-' + (12 - this.labelmd);
43523 if(this.labelsm > 0){
43524 label.cls += ' col-sm-' + this.labelsm;
43525 container.cls += ' col-sm-' + (12 - this.labelsm);
43527 if(this.labelxs > 0){
43528 label.cls += ' col-xs-' + this.labelxs;
43529 container.cls += ' col-xs-' + (12 - this.labelxs);
43540 var settings = this;
43542 ['xs','sm','md','lg'].map(function(size){
43543 if (settings[size]) {
43544 cfg.cls += ' col-' + size + '-' + settings[size];
43551 initEvents : function()
43553 this.indicator = this.indicatorEl();
43555 this.initCurrencyEvent();
43557 this.initNumberEvent();
43560 initCurrencyEvent : function()
43563 throw "can not find store for combo";
43566 this.store = Roo.factory(this.store, Roo.data);
43567 this.store.parent = this;
43571 this.triggerEl = this.el.select('.input-group-addon', true).first();
43573 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43578 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43579 _this.list.setWidth(lw);
43582 this.list.on('mouseover', this.onViewOver, this);
43583 this.list.on('mousemove', this.onViewMove, this);
43584 this.list.on('scroll', this.onViewScroll, this);
43587 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43590 this.view = new Roo.View(this.list, this.tpl, {
43591 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43594 this.view.on('click', this.onViewClick, this);
43596 this.store.on('beforeload', this.onBeforeLoad, this);
43597 this.store.on('load', this.onLoad, this);
43598 this.store.on('loadexception', this.onLoadException, this);
43600 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43601 "up" : function(e){
43602 this.inKeyMode = true;
43606 "down" : function(e){
43607 if(!this.isExpanded()){
43608 this.onTriggerClick();
43610 this.inKeyMode = true;
43615 "enter" : function(e){
43618 if(this.fireEvent("specialkey", this, e)){
43619 this.onViewClick(false);
43625 "esc" : function(e){
43629 "tab" : function(e){
43632 if(this.fireEvent("specialkey", this, e)){
43633 this.onViewClick(false);
43641 doRelay : function(foo, bar, hname){
43642 if(hname == 'down' || this.scope.isExpanded()){
43643 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43651 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43655 initNumberEvent : function(e)
43657 this.inputEl().on("keydown" , this.fireKey, this);
43658 this.inputEl().on("focus", this.onFocus, this);
43659 this.inputEl().on("blur", this.onBlur, this);
43661 this.inputEl().relayEvent('keyup', this);
43663 if(this.indicator){
43664 this.indicator.addClass('invisible');
43667 this.originalValue = this.getValue();
43669 if(this.validationEvent == 'keyup'){
43670 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43671 this.inputEl().on('keyup', this.filterValidation, this);
43673 else if(this.validationEvent !== false){
43674 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43677 if(this.selectOnFocus){
43678 this.on("focus", this.preFocus, this);
43681 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43682 this.inputEl().on("keypress", this.filterKeys, this);
43684 this.inputEl().relayEvent('keypress', this);
43687 var allowed = "0123456789";
43689 if(this.allowDecimals){
43690 allowed += this.decimalSeparator;
43693 if(this.allowNegative){
43697 if(this.thousandsDelimiter) {
43701 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43703 var keyPress = function(e){
43705 var k = e.getKey();
43707 var c = e.getCharCode();
43710 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43711 allowed.indexOf(String.fromCharCode(c)) === -1
43717 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43721 if(allowed.indexOf(String.fromCharCode(c)) === -1){
43726 this.inputEl().on("keypress", keyPress, this);
43730 onTriggerClick : function(e)
43737 this.loadNext = false;
43739 if(this.isExpanded()){
43744 this.hasFocus = true;
43746 if(this.triggerAction == 'all') {
43747 this.doQuery(this.allQuery, true);
43751 this.doQuery(this.getRawValue());
43754 getCurrency : function()
43756 var v = this.currencyEl().getValue();
43761 restrictHeight : function()
43763 this.list.alignTo(this.currencyEl(), this.listAlign);
43764 this.list.alignTo(this.currencyEl(), this.listAlign);
43767 onViewClick : function(view, doFocus, el, e)
43769 var index = this.view.getSelectedIndexes()[0];
43771 var r = this.store.getAt(index);
43774 this.onSelect(r, index);
43778 onSelect : function(record, index){
43780 if(this.fireEvent('beforeselect', this, record, index) !== false){
43782 this.setFromCurrencyData(index > -1 ? record.data : false);
43786 this.fireEvent('select', this, record, index);
43790 setFromCurrencyData : function(o)
43794 this.lastCurrency = o;
43796 if (this.currencyField) {
43797 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43799 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
43802 this.lastSelectionText = currency;
43804 //setting default currency
43805 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43806 this.setCurrency(this.defaultCurrency);
43810 this.setCurrency(currency);
43813 setFromData : function(o)
43817 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43819 this.setFromCurrencyData(c);
43824 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43826 Roo.log('no value set for '+ (this.name ? this.name : this.id));
43829 this.setValue(value);
43833 setCurrency : function(v)
43835 this.currencyValue = v;
43838 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43843 setValue : function(v)
43845 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43851 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43853 this.inputEl().dom.value = (v == '') ? '' :
43854 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43856 if(!this.allowZero && v === '0') {
43857 this.hiddenEl().dom.value = '';
43858 this.inputEl().dom.value = '';
43865 getRawValue : function()
43867 var v = this.inputEl().getValue();
43872 getValue : function()
43874 return this.fixPrecision(this.parseValue(this.getRawValue()));
43877 parseValue : function(value)
43879 if(this.thousandsDelimiter) {
43881 r = new RegExp(",", "g");
43882 value = value.replace(r, "");
43885 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43886 return isNaN(value) ? '' : value;
43890 fixPrecision : function(value)
43892 if(this.thousandsDelimiter) {
43894 r = new RegExp(",", "g");
43895 value = value.replace(r, "");
43898 var nan = isNaN(value);
43900 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43901 return nan ? '' : value;
43903 return parseFloat(value).toFixed(this.decimalPrecision);
43906 decimalPrecisionFcn : function(v)
43908 return Math.floor(v);
43911 validateValue : function(value)
43913 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43917 var num = this.parseValue(value);
43920 this.markInvalid(String.format(this.nanText, value));
43924 if(num < this.minValue){
43925 this.markInvalid(String.format(this.minText, this.minValue));
43929 if(num > this.maxValue){
43930 this.markInvalid(String.format(this.maxText, this.maxValue));
43937 validate : function()
43939 if(this.disabled || this.allowBlank){
43944 var currency = this.getCurrency();
43946 if(this.validateValue(this.getRawValue()) && currency.length){
43951 this.markInvalid();
43955 getName: function()
43960 beforeBlur : function()
43966 var v = this.parseValue(this.getRawValue());
43973 onBlur : function()
43977 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43978 //this.el.removeClass(this.focusClass);
43981 this.hasFocus = false;
43983 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43987 var v = this.getValue();
43989 if(String(v) !== String(this.startValue)){
43990 this.fireEvent('change', this, v, this.startValue);
43993 this.fireEvent("blur", this);
43996 inputEl : function()
43998 return this.el.select('.roo-money-amount-input', true).first();
44001 currencyEl : function()
44003 return this.el.select('.roo-money-currency-input', true).first();
44006 hiddenEl : function()
44008 return this.el.select('input.hidden-number-input',true).first();
44012 * @class Roo.bootstrap.BezierSignature
44013 * @extends Roo.bootstrap.Component
44014 * Bootstrap BezierSignature class
44015 * This script refer to:
44016 * Title: Signature Pad
44018 * Availability: https://github.com/szimek/signature_pad
44021 * Create a new BezierSignature
44022 * @param {Object} config The config object
44025 Roo.bootstrap.BezierSignature = function(config){
44026 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44032 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44039 mouse_btn_down: true,
44042 * @cfg {int} canvas height
44044 canvas_height: '200px',
44047 * @cfg {float|function} Radius of a single dot.
44052 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44057 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44062 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44067 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44072 * @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.
44074 bg_color: 'rgba(0, 0, 0, 0)',
44077 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44079 dot_color: 'black',
44082 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44084 velocity_filter_weight: 0.7,
44087 * @cfg {function} Callback when stroke begin.
44092 * @cfg {function} Callback when stroke end.
44096 getAutoCreate : function()
44098 var cls = 'roo-signature column';
44101 cls += ' ' + this.cls;
44111 for(var i = 0; i < col_sizes.length; i++) {
44112 if(this[col_sizes[i]]) {
44113 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44123 cls: 'roo-signature-body',
44127 cls: 'roo-signature-body-canvas',
44128 height: this.canvas_height,
44129 width: this.canvas_width
44136 style: 'display: none'
44144 initEvents: function()
44146 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44148 var canvas = this.canvasEl();
44150 // mouse && touch event swapping...
44151 canvas.dom.style.touchAction = 'none';
44152 canvas.dom.style.msTouchAction = 'none';
44154 this.mouse_btn_down = false;
44155 canvas.on('mousedown', this._handleMouseDown, this);
44156 canvas.on('mousemove', this._handleMouseMove, this);
44157 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44159 if (window.PointerEvent) {
44160 canvas.on('pointerdown', this._handleMouseDown, this);
44161 canvas.on('pointermove', this._handleMouseMove, this);
44162 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44165 if ('ontouchstart' in window) {
44166 canvas.on('touchstart', this._handleTouchStart, this);
44167 canvas.on('touchmove', this._handleTouchMove, this);
44168 canvas.on('touchend', this._handleTouchEnd, this);
44171 Roo.EventManager.onWindowResize(this.resize, this, true);
44173 // file input event
44174 this.fileEl().on('change', this.uploadImage, this);
44181 resize: function(){
44183 var canvas = this.canvasEl().dom;
44184 var ctx = this.canvasElCtx();
44185 var img_data = false;
44187 if(canvas.width > 0) {
44188 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44190 // setting canvas width will clean img data
44193 var style = window.getComputedStyle ?
44194 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44196 var padding_left = parseInt(style.paddingLeft) || 0;
44197 var padding_right = parseInt(style.paddingRight) || 0;
44199 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44202 ctx.putImageData(img_data, 0, 0);
44206 _handleMouseDown: function(e)
44208 if (e.browserEvent.which === 1) {
44209 this.mouse_btn_down = true;
44210 this.strokeBegin(e);
44214 _handleMouseMove: function (e)
44216 if (this.mouse_btn_down) {
44217 this.strokeMoveUpdate(e);
44221 _handleMouseUp: function (e)
44223 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44224 this.mouse_btn_down = false;
44229 _handleTouchStart: function (e) {
44231 e.preventDefault();
44232 if (e.browserEvent.targetTouches.length === 1) {
44233 // var touch = e.browserEvent.changedTouches[0];
44234 // this.strokeBegin(touch);
44236 this.strokeBegin(e); // assume e catching the correct xy...
44240 _handleTouchMove: function (e) {
44241 e.preventDefault();
44242 // var touch = event.targetTouches[0];
44243 // _this._strokeMoveUpdate(touch);
44244 this.strokeMoveUpdate(e);
44247 _handleTouchEnd: function (e) {
44248 var wasCanvasTouched = e.target === this.canvasEl().dom;
44249 if (wasCanvasTouched) {
44250 e.preventDefault();
44251 // var touch = event.changedTouches[0];
44252 // _this._strokeEnd(touch);
44257 reset: function () {
44258 this._lastPoints = [];
44259 this._lastVelocity = 0;
44260 this._lastWidth = (this.min_width + this.max_width) / 2;
44261 this.canvasElCtx().fillStyle = this.dot_color;
44264 strokeMoveUpdate: function(e)
44266 this.strokeUpdate(e);
44268 if (this.throttle) {
44269 this.throttleStroke(this.strokeUpdate, this.throttle);
44272 this.strokeUpdate(e);
44276 strokeBegin: function(e)
44278 var newPointGroup = {
44279 color: this.dot_color,
44283 if (typeof this.onBegin === 'function') {
44287 this.curve_data.push(newPointGroup);
44289 this.strokeUpdate(e);
44292 strokeUpdate: function(e)
44294 var rect = this.canvasEl().dom.getBoundingClientRect();
44295 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44296 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44297 var lastPoints = lastPointGroup.points;
44298 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44299 var isLastPointTooClose = lastPoint
44300 ? point.distanceTo(lastPoint) <= this.min_distance
44302 var color = lastPointGroup.color;
44303 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44304 var curve = this.addPoint(point);
44306 this.drawDot({color: color, point: point});
44309 this.drawCurve({color: color, curve: curve});
44319 strokeEnd: function(e)
44321 this.strokeUpdate(e);
44322 if (typeof this.onEnd === 'function') {
44327 addPoint: function (point) {
44328 var _lastPoints = this._lastPoints;
44329 _lastPoints.push(point);
44330 if (_lastPoints.length > 2) {
44331 if (_lastPoints.length === 3) {
44332 _lastPoints.unshift(_lastPoints[0]);
44334 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44335 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44336 _lastPoints.shift();
44342 calculateCurveWidths: function (startPoint, endPoint) {
44343 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44344 (1 - this.velocity_filter_weight) * this._lastVelocity;
44346 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44349 start: this._lastWidth
44352 this._lastVelocity = velocity;
44353 this._lastWidth = newWidth;
44357 drawDot: function (_a) {
44358 var color = _a.color, point = _a.point;
44359 var ctx = this.canvasElCtx();
44360 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44362 this.drawCurveSegment(point.x, point.y, width);
44364 ctx.fillStyle = color;
44368 drawCurve: function (_a) {
44369 var color = _a.color, curve = _a.curve;
44370 var ctx = this.canvasElCtx();
44371 var widthDelta = curve.endWidth - curve.startWidth;
44372 var drawSteps = Math.floor(curve.length()) * 2;
44374 ctx.fillStyle = color;
44375 for (var i = 0; i < drawSteps; i += 1) {
44376 var t = i / drawSteps;
44382 var x = uuu * curve.startPoint.x;
44383 x += 3 * uu * t * curve.control1.x;
44384 x += 3 * u * tt * curve.control2.x;
44385 x += ttt * curve.endPoint.x;
44386 var y = uuu * curve.startPoint.y;
44387 y += 3 * uu * t * curve.control1.y;
44388 y += 3 * u * tt * curve.control2.y;
44389 y += ttt * curve.endPoint.y;
44390 var width = curve.startWidth + ttt * widthDelta;
44391 this.drawCurveSegment(x, y, width);
44397 drawCurveSegment: function (x, y, width) {
44398 var ctx = this.canvasElCtx();
44400 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44401 this.is_empty = false;
44406 var ctx = this.canvasElCtx();
44407 var canvas = this.canvasEl().dom;
44408 ctx.fillStyle = this.bg_color;
44409 ctx.clearRect(0, 0, canvas.width, canvas.height);
44410 ctx.fillRect(0, 0, canvas.width, canvas.height);
44411 this.curve_data = [];
44413 this.is_empty = true;
44418 return this.el.select('input',true).first();
44421 canvasEl: function()
44423 return this.el.select('canvas',true).first();
44426 canvasElCtx: function()
44428 return this.el.select('canvas',true).first().dom.getContext('2d');
44431 getImage: function(type)
44433 if(this.is_empty) {
44438 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44441 drawFromImage: function(img_src)
44443 var img = new Image();
44445 img.onload = function(){
44446 this.canvasElCtx().drawImage(img, 0, 0);
44451 this.is_empty = false;
44454 selectImage: function()
44456 this.fileEl().dom.click();
44459 uploadImage: function(e)
44461 var reader = new FileReader();
44463 reader.onload = function(e){
44464 var img = new Image();
44465 img.onload = function(){
44467 this.canvasElCtx().drawImage(img, 0, 0);
44469 img.src = e.target.result;
44472 reader.readAsDataURL(e.target.files[0]);
44475 // Bezier Point Constructor
44476 Point: (function () {
44477 function Point(x, y, time) {
44480 this.time = time || Date.now();
44482 Point.prototype.distanceTo = function (start) {
44483 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44485 Point.prototype.equals = function (other) {
44486 return this.x === other.x && this.y === other.y && this.time === other.time;
44488 Point.prototype.velocityFrom = function (start) {
44489 return this.time !== start.time
44490 ? this.distanceTo(start) / (this.time - start.time)
44497 // Bezier Constructor
44498 Bezier: (function () {
44499 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44500 this.startPoint = startPoint;
44501 this.control2 = control2;
44502 this.control1 = control1;
44503 this.endPoint = endPoint;
44504 this.startWidth = startWidth;
44505 this.endWidth = endWidth;
44507 Bezier.fromPoints = function (points, widths, scope) {
44508 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44509 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44510 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44512 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44513 var dx1 = s1.x - s2.x;
44514 var dy1 = s1.y - s2.y;
44515 var dx2 = s2.x - s3.x;
44516 var dy2 = s2.y - s3.y;
44517 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44518 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44519 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44520 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44521 var dxm = m1.x - m2.x;
44522 var dym = m1.y - m2.y;
44523 var k = l2 / (l1 + l2);
44524 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44525 var tx = s2.x - cm.x;
44526 var ty = s2.y - cm.y;
44528 c1: new scope.Point(m1.x + tx, m1.y + ty),
44529 c2: new scope.Point(m2.x + tx, m2.y + ty)
44532 Bezier.prototype.length = function () {
44537 for (var i = 0; i <= steps; i += 1) {
44539 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44540 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44542 var xdiff = cx - px;
44543 var ydiff = cy - py;
44544 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44551 Bezier.prototype.point = function (t, start, c1, c2, end) {
44552 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44553 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44554 + (3.0 * c2 * (1.0 - t) * t * t)
44555 + (end * t * t * t);
44560 throttleStroke: function(fn, wait) {
44561 if (wait === void 0) { wait = 250; }
44563 var timeout = null;
44567 var later = function () {
44568 previous = Date.now();
44570 result = fn.apply(storedContext, storedArgs);
44572 storedContext = null;
44576 return function wrapper() {
44578 for (var _i = 0; _i < arguments.length; _i++) {
44579 args[_i] = arguments[_i];
44581 var now = Date.now();
44582 var remaining = wait - (now - previous);
44583 storedContext = this;
44585 if (remaining <= 0 || remaining > wait) {
44587 clearTimeout(timeout);
44591 result = fn.apply(storedContext, storedArgs);
44593 storedContext = null;
44597 else if (!timeout) {
44598 timeout = window.setTimeout(later, remaining);